mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Create a dedicated crate
This commit is contained in:
parent
38ee6ec800
commit
93d444cca4
22 changed files with 289 additions and 143 deletions
|
@ -22,6 +22,7 @@ uv-configuration = { workspace = true }
|
|||
uv-distribution = { workspace = true }
|
||||
uv-distribution-types = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
|
|
|
@ -32,8 +32,8 @@ use uv_configuration::PreviewMode;
|
|||
use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, SourceStrategy};
|
||||
use uv_distribution::BuildRequires;
|
||||
use uv_distribution_types::{IndexLocations, Requirement, Resolution};
|
||||
use uv_fs::LockedFile;
|
||||
use uv_fs::{PythonExt, Simplified};
|
||||
use uv_lock::LockedFile;
|
||||
use uv_pep440::Version;
|
||||
use uv_pep508::PackageName;
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
|
|
|
@ -22,6 +22,7 @@ uv-cache-key = { workspace = true }
|
|||
uv-dirs = { workspace = true }
|
||||
uv-distribution-types = { workspace = true }
|
||||
uv-fs = { workspace = true, features = ["tokio"] }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-redacted = { workspace = true }
|
||||
|
|
|
@ -11,7 +11,8 @@ use tracing::debug;
|
|||
|
||||
pub use archive::ArchiveId;
|
||||
use uv_cache_info::Timestamp;
|
||||
use uv_fs::{LockedFile, cachedir, directories};
|
||||
use uv_fs::{cachedir, directories};
|
||||
use uv_lock::LockedFile;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pypi_types::ResolutionMetadata;
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use fs2::FileExt;
|
||||
use tempfile::NamedTempFile;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use tracing::warn;
|
||||
|
||||
pub use crate::path::*;
|
||||
|
||||
|
@ -599,137 +597,6 @@ pub fn is_virtualenv_base(path: impl AsRef<Path>) -> bool {
|
|||
path.as_ref().join("pyvenv.cfg").is_file()
|
||||
}
|
||||
|
||||
/// A file lock that is automatically released when dropped.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct LockedFile(fs_err::File);
|
||||
|
||||
impl LockedFile {
|
||||
/// Inner implementation for [`LockedFile::acquire_blocking`] and [`LockedFile::acquire`].
|
||||
fn lock_file_blocking(file: fs_err::File, resource: &str) -> Result<Self, std::io::Error> {
|
||||
trace!(
|
||||
"Checking lock for `{resource}` at `{}`",
|
||||
file.path().user_display()
|
||||
);
|
||||
match file.file().try_lock_exclusive() {
|
||||
Ok(()) => {
|
||||
debug!("Acquired lock for `{resource}`");
|
||||
Ok(Self(file))
|
||||
}
|
||||
Err(err) => {
|
||||
// Log error code and enum kind to help debugging more exotic failures.
|
||||
if err.kind() != std::io::ErrorKind::WouldBlock {
|
||||
debug!("Try lock error: {err:?}");
|
||||
}
|
||||
info!(
|
||||
"Waiting to acquire lock for `{resource}` at `{}`",
|
||||
file.path().user_display(),
|
||||
);
|
||||
file.file().lock_exclusive().map_err(|err| {
|
||||
// Not an fs_err method, we need to build our own path context
|
||||
std::io::Error::other(format!(
|
||||
"Could not acquire lock for `{resource}` at `{}`: {}",
|
||||
file.path().user_display(),
|
||||
err
|
||||
))
|
||||
})?;
|
||||
|
||||
debug!("Acquired lock for `{resource}`");
|
||||
Ok(Self(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The same as [`LockedFile::acquire`], but for synchronous contexts. Do not use from an async
|
||||
/// context, as this can block the runtime while waiting for another process to release the
|
||||
/// lock.
|
||||
pub fn acquire_blocking(
|
||||
path: impl AsRef<Path>,
|
||||
resource: impl Display,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let file = Self::create(path)?;
|
||||
let resource = resource.to_string();
|
||||
Self::lock_file_blocking(file, &resource)
|
||||
}
|
||||
|
||||
/// Acquire a cross-process lock for a resource using a file at the provided path.
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn acquire(
|
||||
path: impl AsRef<Path>,
|
||||
resource: impl Display,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let file = Self::create(path)?;
|
||||
let resource = resource.to_string();
|
||||
tokio::task::spawn_blocking(move || Self::lock_file_blocking(file, &resource)).await?
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, std::io::Error> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
// If path already exists, return it.
|
||||
if let Ok(file) = fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path.as_ref())
|
||||
{
|
||||
return Ok(file);
|
||||
}
|
||||
|
||||
// Otherwise, create a temporary file with 777 permissions. We must set
|
||||
// permissions _after_ creating the file, to override the `umask`.
|
||||
let file = if let Some(parent) = path.as_ref().parent() {
|
||||
NamedTempFile::new_in(parent)?
|
||||
} else {
|
||||
NamedTempFile::new()?
|
||||
};
|
||||
if let Err(err) = file
|
||||
.as_file()
|
||||
.set_permissions(std::fs::Permissions::from_mode(0o777))
|
||||
{
|
||||
warn!("Failed to set permissions on temporary file: {err}");
|
||||
}
|
||||
|
||||
// Try to move the file to path, but if path exists now, just open path
|
||||
match file.persist_noclobber(path.as_ref()) {
|
||||
Ok(file) => Ok(fs_err::File::from_parts(file, path.as_ref())),
|
||||
Err(err) => {
|
||||
if err.error.kind() == std::io::ErrorKind::AlreadyExists {
|
||||
fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path.as_ref())
|
||||
} else {
|
||||
Err(err.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn create(path: impl AsRef<Path>) -> std::io::Result<fs_err::File> {
|
||||
fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LockedFile {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = fs2::FileExt::unlock(self.0.file()) {
|
||||
error!(
|
||||
"Failed to unlock {}; program may be stuck: {}",
|
||||
self.0.path().display(),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
debug!("Released lock at `{}`", self.0.path().display());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An asynchronous reader that reports progress as bytes are read.
|
||||
#[cfg(feature = "tokio")]
|
||||
pub struct ProgressReader<Reader: tokio::io::AsyncRead + Unpin, Callback: Fn(usize) + Unpin> {
|
||||
|
|
|
@ -20,6 +20,7 @@ uv-auth = { workspace = true }
|
|||
uv-cache-key = { workspace = true }
|
||||
uv-fs = { workspace = true, features = ["tokio"] }
|
||||
uv-git-types = { workspace = true }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-redacted = { workspace = true }
|
||||
uv-static = { workspace = true }
|
||||
uv-version = { workspace = true }
|
||||
|
|
|
@ -10,8 +10,8 @@ use reqwest_middleware::ClientWithMiddleware;
|
|||
use tracing::debug;
|
||||
|
||||
use uv_cache_key::{RepositoryUrl, cache_digest};
|
||||
use uv_fs::LockedFile;
|
||||
use uv_git_types::{GitHubRepository, GitOid, GitReference, GitUrl};
|
||||
use uv_lock::LockedFile;
|
||||
use uv_static::EnvVars;
|
||||
use uv_version::version;
|
||||
|
||||
|
|
31
crates/uv-lock/Cargo.toml
Normal file
31
crates/uv-lock/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "uv-lock"
|
||||
version = "0.0.1"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
uv-fs = { workspace = true }
|
||||
uv-state = { workspace = true }
|
||||
uv-static = { workspace = true }
|
||||
|
||||
fs-err = { workspace = true }
|
||||
fs2 = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
tokio = { workspace = true, optional = true}
|
||||
tracing = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
tokio = ["dep:tokio", "fs-err/tokio"]
|
208
crates/uv-lock/src/lib.rs
Normal file
208
crates/uv-lock/src/lib.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
use fs_err as fs;
|
||||
use std::fmt::Display;
|
||||
use std::io::Write;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use fs2::FileExt;
|
||||
use tempfile::NamedTempFile;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use uv_fs::Simplified;
|
||||
use uv_state::{StateBucket, StateStore};
|
||||
use uv_static::EnvVars;
|
||||
|
||||
/// Filesystem locks used to synchronize access to shared resources across processes.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FilesystemLocks {
|
||||
/// The path to the top-level directory of the filesystem locks.
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
impl FilesystemLocks {
|
||||
/// A directory for filesystem locks at `root`.
|
||||
fn from_path(root: impl Into<PathBuf>) -> Self {
|
||||
Self { root: root.into() }
|
||||
}
|
||||
|
||||
/// Create a new [`FilesystemLocks`] from settings.
|
||||
///
|
||||
/// Prefer, in order:
|
||||
///
|
||||
/// 1. The specific tool directory specified by the user, i.e., `UV_LOCK_DIR`
|
||||
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/tools`
|
||||
/// 3. A directory in the local data directory, e.g., `./.uv/tools`
|
||||
pub fn from_settings() -> Result<Self, std::io::Error> {
|
||||
if let Some(lock_dir) = std::env::var_os(EnvVars::UV_LOCK_DIR).filter(|s| !s.is_empty()) {
|
||||
Ok(Self::from_path(std::path::absolute(lock_dir)?))
|
||||
} else {
|
||||
Ok(Self::from_path(
|
||||
StateStore::from_settings(None)?.bucket(StateBucket::Locks),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a temporary directory.
|
||||
pub fn temp() -> Result<Self, std::io::Error> {
|
||||
Ok(Self::from_path(
|
||||
StateStore::temp()?.bucket(StateBucket::Locks),
|
||||
))
|
||||
}
|
||||
|
||||
/// Initialize the directory.
|
||||
pub fn init(self) -> Result<Self, std::io::Error> {
|
||||
let root = &self.root;
|
||||
|
||||
// Create the tools directory, if it doesn't exist.
|
||||
fs::create_dir_all(root)?;
|
||||
|
||||
// Add a .gitignore.
|
||||
match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(root.join(".gitignore"))
|
||||
{
|
||||
Ok(mut file) => file.write_all(b"*")?,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => (),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Return the path of the directory.
|
||||
pub fn root(&self) -> &Path {
|
||||
&self.root
|
||||
}
|
||||
}
|
||||
|
||||
/// A file lock that is automatically released when dropped.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct LockedFile(fs_err::File);
|
||||
|
||||
impl LockedFile {
|
||||
/// Inner implementation for [`LockedFile::acquire_blocking`] and [`LockedFile::acquire`].
|
||||
fn lock_file_blocking(file: fs_err::File, resource: &str) -> Result<Self, std::io::Error> {
|
||||
trace!(
|
||||
"Checking lock for `{resource}` at `{}`",
|
||||
file.path().user_display()
|
||||
);
|
||||
match file.file().try_lock_exclusive() {
|
||||
Ok(()) => {
|
||||
debug!("Acquired lock for `{resource}`");
|
||||
Ok(Self(file))
|
||||
}
|
||||
Err(err) => {
|
||||
// Log error code and enum kind to help debugging more exotic failures.
|
||||
if err.kind() != std::io::ErrorKind::WouldBlock {
|
||||
debug!("Try lock error: {err:?}");
|
||||
}
|
||||
info!(
|
||||
"Waiting to acquire lock for `{resource}` at `{}`",
|
||||
file.path().user_display(),
|
||||
);
|
||||
file.file().lock_exclusive().map_err(|err| {
|
||||
// Not an fs_err method, we need to build our own path context
|
||||
std::io::Error::other(format!(
|
||||
"Could not acquire lock for `{resource}` at `{}`: {}",
|
||||
file.path().user_display(),
|
||||
err
|
||||
))
|
||||
})?;
|
||||
|
||||
debug!("Acquired lock for `{resource}`");
|
||||
Ok(Self(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The same as [`LockedFile::acquire`], but for synchronous contexts. Do not use from an async
|
||||
/// context, as this can block the runtime while waiting for another process to release the
|
||||
/// lock.
|
||||
pub fn acquire_blocking(
|
||||
path: impl AsRef<Path>,
|
||||
resource: impl Display,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let file = Self::create(path)?;
|
||||
let resource = resource.to_string();
|
||||
Self::lock_file_blocking(file, &resource)
|
||||
}
|
||||
|
||||
/// Acquire a cross-process lock for a resource using a file at the provided path.
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn acquire(
|
||||
path: impl AsRef<Path>,
|
||||
resource: impl Display,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
let file = Self::create(path)?;
|
||||
let resource = resource.to_string();
|
||||
tokio::task::spawn_blocking(move || Self::lock_file_blocking(file, &resource)).await?
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, std::io::Error> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
// If path already exists, return it.
|
||||
if let Ok(file) = fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path.as_ref())
|
||||
{
|
||||
return Ok(file);
|
||||
}
|
||||
|
||||
// Otherwise, create a temporary file with 777 permissions. We must set
|
||||
// permissions _after_ creating the file, to override the `umask`.
|
||||
let file = if let Some(parent) = path.as_ref().parent() {
|
||||
NamedTempFile::new_in(parent)?
|
||||
} else {
|
||||
NamedTempFile::new()?
|
||||
};
|
||||
if let Err(err) = file
|
||||
.as_file()
|
||||
.set_permissions(std::fs::Permissions::from_mode(0o777))
|
||||
{
|
||||
warn!("Failed to set permissions on temporary file: {err}");
|
||||
}
|
||||
|
||||
// Try to move the file to path, but if path exists now, just open path
|
||||
match file.persist_noclobber(path.as_ref()) {
|
||||
Ok(file) => Ok(fs_err::File::from_parts(file, path.as_ref())),
|
||||
Err(err) => {
|
||||
if err.error.kind() == std::io::ErrorKind::AlreadyExists {
|
||||
fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path.as_ref())
|
||||
} else {
|
||||
Err(err.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn create(path: impl AsRef<Path>) -> std::io::Result<fs_err::File> {
|
||||
fs_err::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LockedFile {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = fs2::FileExt::unlock(self.0.file()) {
|
||||
error!(
|
||||
"Failed to unlock {}; program may be stuck: {}",
|
||||
self.0.path().display(),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
debug!("Released lock at `{}`", self.0.path().display());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ uv-distribution-filename = { workspace = true }
|
|||
uv-extract = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-install-wheel = { workspace = true }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-platform-tags = { workspace = true }
|
||||
|
|
|
@ -8,7 +8,8 @@ use tracing::debug;
|
|||
|
||||
use uv_cache::Cache;
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
use uv_fs::Simplified;
|
||||
use uv_lock::LockedFile;
|
||||
use uv_pep440::Version;
|
||||
|
||||
use crate::discovery::find_python_installation;
|
||||
|
|
|
@ -17,8 +17,9 @@ use tracing::{debug, trace, warn};
|
|||
use uv_cache::{Cache, CacheBucket, CachedByTimestamp, Freshness};
|
||||
use uv_cache_info::Timestamp;
|
||||
use uv_cache_key::cache_digest;
|
||||
use uv_fs::{LockedFile, PythonExt, Simplified, write_atomic_sync};
|
||||
use uv_fs::{PythonExt, Simplified, write_atomic_sync};
|
||||
use uv_install_wheel::Layout;
|
||||
use uv_lock::LockedFile;
|
||||
use uv_pep440::Version;
|
||||
use uv_pep508::{MarkerEnvironment, StringVersion};
|
||||
use uv_platform_tags::Platform;
|
||||
|
|
|
@ -16,7 +16,8 @@ use uv_configuration::PreviewMode;
|
|||
#[cfg(windows)]
|
||||
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
|
||||
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
|
||||
use uv_fs::{Simplified, replace_symlink, symlink_or_copy_file};
|
||||
use uv_lock::LockedFile;
|
||||
use uv_state::{StateBucket, StateStore};
|
||||
use uv_static::EnvVars;
|
||||
use uv_trampoline_builder::{Launcher, windows_python_launcher};
|
||||
|
|
|
@ -105,6 +105,8 @@ pub enum StateBucket {
|
|||
ManagedPython,
|
||||
/// Installed tools.
|
||||
Tools,
|
||||
/// File-system locks.
|
||||
Locks,
|
||||
}
|
||||
|
||||
impl StateBucket {
|
||||
|
@ -112,6 +114,7 @@ impl StateBucket {
|
|||
match self {
|
||||
Self::ManagedPython => "python",
|
||||
Self::Tools => "tools",
|
||||
Self::Locks => "locks",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,6 +247,9 @@ impl EnvVars {
|
|||
/// Specifies the directory where uv stores managed tools.
|
||||
pub const UV_TOOL_DIR: &'static str = "UV_TOOL_DIR";
|
||||
|
||||
/// Specifies the directory where uv stores filesystem locks.
|
||||
pub const UV_LOCK_DIR: &'static str = "UV_LOCK_DIR";
|
||||
|
||||
/// Specifies the "bin" directory for installing tool executables.
|
||||
pub const UV_TOOL_BIN_DIR: &'static str = "UV_TOOL_BIN_DIR";
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ uv-distribution-types = { workspace = true }
|
|||
uv-fs = { workspace = true }
|
||||
uv-install-wheel = { workspace = true }
|
||||
uv-installer = { workspace = true }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
|
|
|
@ -19,8 +19,9 @@ use uv_install_wheel::read_record_file;
|
|||
pub use receipt::ToolReceipt;
|
||||
pub use tool::{Tool, ToolEntrypoint};
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_lock::LockedFile;
|
||||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
use uv_state::{StateBucket, StateStore};
|
||||
use uv_static::EnvVars;
|
||||
|
|
|
@ -35,6 +35,7 @@ uv-git = { workspace = true }
|
|||
uv-git-types = { workspace = true }
|
||||
uv-install-wheel = { workspace = true, default-features = false }
|
||||
uv-installer = { workspace = true }
|
||||
uv-lock = { workspace = true, features = ["tokio"] }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
|
|
|
@ -27,9 +27,10 @@ use uv_distribution_types::{
|
|||
Index, IndexName, IndexUrl, IndexUrls, NameRequirementSpecification, Requirement,
|
||||
RequirementSource, UnresolvedRequirement, VersionId,
|
||||
};
|
||||
use uv_fs::{LockedFile, Simplified};
|
||||
use uv_fs::Simplified;
|
||||
use uv_git::GIT_STORE;
|
||||
use uv_git_types::GitReference;
|
||||
use uv_lock::LockedFile;
|
||||
use uv_normalize::{DEV_DEPENDENCIES, DefaultExtras, DefaultGroups, PackageName};
|
||||
use uv_pep508::{ExtraName, MarkerTree, UnnamedRequirement, VersionOrUrl};
|
||||
use uv_pypi_types::{ParsedUrl, VerbatimParsedUrl};
|
||||
|
|
|
@ -21,9 +21,10 @@ use uv_distribution_types::{
|
|||
Index, Requirement, RequiresPython, Resolution, UnresolvedRequirement,
|
||||
UnresolvedRequirementSpecification,
|
||||
};
|
||||
use uv_fs::{CWD, LockedFile, Simplified};
|
||||
use uv_fs::{CWD, Simplified};
|
||||
use uv_git::ResolvedRepositoryReference;
|
||||
use uv_installer::{SatisfiesResult, SitePackages};
|
||||
use uv_lock::LockedFile;
|
||||
use uv_normalize::{DEV_DEPENDENCIES, DefaultGroups, ExtraName, GroupName, PackageName};
|
||||
use uv_pep440::{TildeVersionSpecifier, Version, VersionSpecifiers};
|
||||
use uv_pep508::MarkerTreeContents;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue