Skip to content

Commit

Permalink
Add support for uv tree
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Dec 25, 2024
1 parent 25b8fe7 commit 0f65988
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 11 deletions.
8 changes: 8 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3417,6 +3417,14 @@ pub struct TreeArgs {
#[command(flatten)]
pub resolver: ResolverArgs,

/// Show the dependency tree the specified PEP 723 Python script, rather than the current
/// project.
///
/// If provided, uv will resolve the dependencies based on its inline metadata table, in
/// adherence with PEP 723.
#[arg(long)]
pub script: Option<PathBuf>,

/// The Python version to use when filtering the tree.
///
/// For example, pass `--python-version 3.10` to display the dependencies
Expand Down
8 changes: 8 additions & 0 deletions crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ pub(crate) enum ProjectError {
#[error("Group `{0}` is not defined in any project's `dependency-group` table")]
MissingGroupWorkspace(GroupName),

#[error("PEP 723 scripts do not support dependency groups, but group `{0}` was specified")]
MissingGroupScript(GroupName),

#[error("Default group `{0}` (from `tool.uv.default-groups`) is not defined in the project's `dependency-group` table")]
MissingDefaultGroup(GroupName),

Expand Down Expand Up @@ -1729,6 +1732,8 @@ pub(crate) enum DependencyGroupsTarget<'env> {
Workspace(&'env Workspace),
/// The dependency groups must be defined in the target project.
Project(&'env ProjectWorkspace),
/// The dependency groups must be defined in the target script.
Script,
}

impl DependencyGroupsTarget<'_> {
Expand Down Expand Up @@ -1759,6 +1764,9 @@ impl DependencyGroupsTarget<'_> {
return Err(ProjectError::MissingGroupProject(group.clone()));
}
}
Self::Script => {
return Err(ProjectError::MissingGroupScript(group.clone()));
}
}
}
Ok(())
Expand Down
51 changes: 41 additions & 10 deletions crates/uv/src/commands/project/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ use uv_distribution_types::IndexCapabilities;
use uv_pep508::PackageName;
use uv_python::{PythonDownloads, PythonPreference, PythonRequest, PythonVersion};
use uv_resolver::{PackageMap, TreeDisplay};
use uv_scripts::{Pep723Item, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_workspace::{DiscoveryOptions, Workspace};

use crate::commands::pip::latest::LatestClient;
use crate::commands::pip::loggers::DefaultResolveLogger;
use crate::commands::pip::resolution_markers;
use crate::commands::project::lock::{do_safe_lock, LockMode};
use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{
default_dependency_groups, DependencyGroupsTarget, ProjectError, ProjectInterpreter,
ScriptInterpreter,
};
use crate::commands::reporters::LatestVersionReporter;
use crate::commands::{diagnostics, ExitStatus};
Expand All @@ -49,6 +52,7 @@ pub(crate) async fn tree(
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
script: Option<Pep723Script>,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
connectivity: Connectivity,
Expand All @@ -61,24 +65,51 @@ pub(crate) async fn tree(
preview: PreviewMode,
) -> Result<ExitStatus> {
// Find the project requirements.
let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
let workspace;
let target = if let Some(script) = script.as_ref() {
LockTarget::Script(script)
} else {
workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
LockTarget::Workspace(&workspace)
};

// Validate that any referenced dependency groups are defined in the workspace.
// Validate that any referenced dependency groups are defined in the target.
if !frozen {
let target = DependencyGroupsTarget::Workspace(&workspace);
let target = match &target {
LockTarget::Workspace(workspace) => DependencyGroupsTarget::Workspace(workspace),
LockTarget::Script(..) => DependencyGroupsTarget::Script,
};
target.validate(&dev)?;
}

// Determine the default groups to include.
let defaults = default_dependency_groups(workspace.pyproject_toml())?;
let defaults = match target {
LockTarget::Workspace(workspace) => default_dependency_groups(workspace.pyproject_toml())?,
LockTarget::Script(_) => vec![],
};

// Find an interpreter for the project, unless `--frozen` and `--universal` are both set.
let interpreter = if frozen && universal {
None
} else {
Some(
ProjectInterpreter::discover(
&workspace,
Some(match target {
LockTarget::Script(script) => ScriptInterpreter::discover(
&Pep723Item::Script(script.clone()),
python.as_deref().map(PythonRequest::parse),
python_preference,
python_downloads,
connectivity,
native_tls,
allow_insecure_host,
&install_mirrors,
no_config,
cache,
printer,
)
.await?
.into_interpreter(),
LockTarget::Workspace(workspace) => ProjectInterpreter::discover(
workspace,
project_dir,
python.as_deref().map(PythonRequest::parse),
python_preference,
Expand All @@ -93,7 +124,7 @@ pub(crate) async fn tree(
)
.await?
.into_interpreter(),
)
})
};

// Determine the lock mode.
Expand All @@ -111,7 +142,7 @@ pub(crate) async fn tree(
// Update the lockfile, if necessary.
let lock = match do_safe_lock(
mode,
(&workspace).into(),
target,
settings.as_ref(),
LowerBound::Allow,
&state,
Expand Down Expand Up @@ -151,7 +182,7 @@ pub(crate) async fn tree(
.packages()
.iter()
.filter_map(|package| {
let index = match package.index(workspace.install_path()) {
let index = match package.index(target.install_path()) {
Ok(Some(index)) => index,
Ok(None) => return None,
Err(err) => return Some(Err(err)),
Expand Down
14 changes: 14 additions & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
script: Some(script),
..
}) = &**command
{
Pep723Script::read(&script).await?.map(Pep723Item::Script)
} else if let ProjectCommand::Tree(uv_cli::TreeArgs {
script: Some(script),
..
}) = &**command
{
Pep723Script::read(&script).await?.map(Pep723Item::Script)
} else {
Expand Down Expand Up @@ -1627,6 +1633,13 @@ async fn run_project(
// Initialize the cache.
let cache = cache.init()?;

// Unwrap the script.
let script = script.map(|script| match script {
Pep723Item::Script(script) => script,
Pep723Item::Stdin(_) => unreachable!("`uv tree` does not support stdin"),
Pep723Item::Remote(_) => unreachable!("`uv tree` does not support remote files"),
});

commands::tree(
project_dir,
args.dev,
Expand All @@ -1644,6 +1657,7 @@ async fn run_project(
args.python,
args.install_mirrors,
args.resolver,
script,
globals.python_preference,
globals.python_downloads,
globals.connectivity,
Expand Down
4 changes: 4 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,8 @@ pub(crate) struct TreeSettings {
pub(crate) no_dedupe: bool,
pub(crate) invert: bool,
pub(crate) outdated: bool,
#[allow(dead_code)]
pub(crate) script: Option<PathBuf>,
pub(crate) python_version: Option<PythonVersion>,
pub(crate) python_platform: Option<TargetTriple>,
pub(crate) python: Option<String>,
Expand All @@ -1297,6 +1299,7 @@ impl TreeSettings {
frozen,
build,
resolver,
script,
python_version,
python_platform,
python,
Expand All @@ -1319,6 +1322,7 @@ impl TreeSettings {
no_dedupe: tree.no_dedupe,
invert: tree.invert,
outdated: tree.outdated,
script,
python_version,
python_platform,
python: python.and_then(Maybe::into_option),
Expand Down
Loading

0 comments on commit 0f65988

Please sign in to comment.