Skip to content

Commit ea79b98

Browse files
committed
Add uv workspace metadata
1 parent a1610c7 commit ea79b98

File tree

12 files changed

+575
-43
lines changed

12 files changed

+575
-43
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,12 @@ pub enum Commands {
517517
Build(BuildArgs),
518518
/// Upload distributions to an index.
519519
Publish(PublishArgs),
520+
/// Manage workspaces.
521+
#[command(
522+
after_help = "Use `uv help workspace` for more details.",
523+
after_long_help = ""
524+
)]
525+
Workspace(WorkspaceNamespace),
520526
/// The implementation of the build backend.
521527
///
522528
/// These commands are not directly exposed to the user, instead users invoke their build
@@ -6773,6 +6779,21 @@ pub struct PublishArgs {
67736779
pub dry_run: bool,
67746780
}
67756781

6782+
#[derive(Args)]
6783+
pub struct WorkspaceNamespace {
6784+
#[command(subcommand)]
6785+
pub command: WorkspaceCommand,
6786+
}
6787+
6788+
#[derive(Subcommand)]
6789+
pub enum WorkspaceCommand {
6790+
/// Display package metadata.
6791+
Metadata(MetadataArgs),
6792+
}
6793+
6794+
#[derive(Args, Debug)]
6795+
pub struct MetadataArgs;
6796+
67766797
/// See [PEP 517](https://peps.python.org/pep-0517/) and
67776798
/// [PEP 660](https://peps.python.org/pep-0660/) for specifications of the parameters.
67786799
#[derive(Subcommand)]

crates/uv-preview/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bitflags::bitflags! {
2020
const FORMAT = 1 << 8;
2121
const NATIVE_AUTH = 1 << 9;
2222
const S3_ENDPOINT = 1 << 10;
23+
const WORKSPACE_METADATA = 1 << 11;
2324
}
2425
}
2526

@@ -40,6 +41,7 @@ impl PreviewFeatures {
4041
Self::FORMAT => "format",
4142
Self::NATIVE_AUTH => "native-auth",
4243
Self::S3_ENDPOINT => "s3-endpoint",
44+
Self::WORKSPACE_METADATA => "workspace-metadata",
4345
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
4446
}
4547
}
@@ -88,6 +90,7 @@ impl FromStr for PreviewFeatures {
8890
"format" => Self::FORMAT,
8991
"native-auth" => Self::NATIVE_AUTH,
9092
"s3-endpoint" => Self::S3_ENDPOINT,
93+
"workspace-metadata" => Self::WORKSPACE_METADATA,
9194
_ => {
9295
warn_user_once!("Unknown preview feature: `{part}`");
9396
continue;

crates/uv/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ use uv_normalize::PackageName;
6666
use uv_python::PythonEnvironment;
6767
use uv_scripts::Pep723Script;
6868
pub(crate) use venv::venv;
69+
pub(crate) use workspace::metadata::metadata;
6970

7071
use crate::printer::Printer;
7172

@@ -86,6 +87,7 @@ pub(crate) mod reporters;
8687
mod self_update;
8788
mod tool;
8889
mod venv;
90+
mod workspace;
8991

9092
#[derive(Copy, Clone)]
9193
pub(crate) enum ExitStatus {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use std::fmt::Write;
2+
use std::path::{Path, PathBuf};
3+
4+
use anyhow::Result;
5+
use serde::Serialize;
6+
7+
use uv_normalize::PackageName;
8+
use uv_preview::{Preview, PreviewFeatures};
9+
use uv_warnings::warn_user;
10+
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};
11+
12+
use crate::commands::ExitStatus;
13+
use crate::printer::Printer;
14+
15+
/// The schema version for the metadata report.
16+
#[derive(Serialize, Debug, Default)]
17+
#[serde(rename_all = "snake_case")]
18+
enum SchemaVersion {
19+
/// An unstable, experimental schema.
20+
#[default]
21+
Preview,
22+
}
23+
24+
/// The schema metadata for the metadata report.
25+
#[derive(Serialize, Debug, Default)]
26+
struct SchemaReport {
27+
/// The version of the schema.
28+
version: SchemaVersion,
29+
}
30+
31+
/// Report for a single workspace member.
32+
#[derive(Serialize, Debug)]
33+
struct WorkspaceMemberReport {
34+
/// The name of the workspace member.
35+
name: PackageName,
36+
/// The path to the workspace member's root directory.
37+
path: PathBuf,
38+
}
39+
40+
/// The report for a metadata operation.
41+
#[derive(Serialize, Debug)]
42+
struct MetadataReport {
43+
/// The schema of this report.
44+
schema: SchemaReport,
45+
/// The workspace root directory.
46+
workspace_root: PathBuf,
47+
/// The workspace members.
48+
members: Vec<WorkspaceMemberReport>,
49+
}
50+
51+
/// Display package metadata.
52+
pub(crate) async fn metadata(
53+
project_dir: &Path,
54+
preview: Preview,
55+
printer: Printer,
56+
) -> Result<ExitStatus> {
57+
if preview.is_enabled(PreviewFeatures::WORKSPACE_METADATA) {
58+
warn_user!(
59+
"The `uv workspace metadata` command is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
60+
PreviewFeatures::WORKSPACE_METADATA
61+
);
62+
}
63+
64+
let workspace_cache = WorkspaceCache::default();
65+
let workspace =
66+
Workspace::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache).await?;
67+
68+
let members = workspace
69+
.packages()
70+
.values()
71+
.map(|package| WorkspaceMemberReport {
72+
name: package.project().name.clone(),
73+
path: package.root().clone(),
74+
})
75+
.collect();
76+
77+
let report = MetadataReport {
78+
schema: SchemaReport::default(),
79+
workspace_root: workspace.install_path().clone(),
80+
members,
81+
};
82+
83+
writeln!(
84+
printer.stdout(),
85+
"{}",
86+
serde_json::to_string_pretty(&report)?
87+
)?;
88+
89+
Ok(ExitStatus::Success)
90+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod metadata;

crates/uv/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use uv_cli::SelfUpdateArgs;
2626
use uv_cli::{
2727
AuthCommand, AuthNamespace, BuildBackendCommand, CacheCommand, CacheNamespace, Cli, Commands,
2828
PipCommand, PipNamespace, ProjectCommand, PythonCommand, PythonNamespace, SelfCommand,
29-
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, compat::CompatArgs,
29+
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, WorkspaceCommand, WorkspaceNamespace,
30+
compat::CompatArgs,
3031
};
3132
use uv_client::BaseClientBuilder;
3233
use uv_configuration::min_stack_size;
@@ -1709,6 +1710,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
17091710
)
17101711
.await
17111712
}
1713+
Commands::Workspace(WorkspaceNamespace { command }) => match command {
1714+
WorkspaceCommand::Metadata(_args) => {
1715+
commands::metadata(&project_dir, globals.preview, printer).await
1716+
}
1717+
},
17121718
Commands::BuildBackend { command } => spawn_blocking(move || match command {
17131719
BuildBackendCommand::BuildSdist { sdist_directory } => {
17141720
commands::build_backend::build_sdist(&sdist_directory)

crates/uv/tests/it/common/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,14 @@ impl TestContext {
10501050
command
10511051
}
10521052

1053+
/// Create a `uv workspace metadata` command with options shared across scenarios.
1054+
pub fn workspace_metadata(&self) -> Command {
1055+
let mut command = Self::new_command();
1056+
command.arg("workspace").arg("metadata");
1057+
self.add_shared_options(&mut command, false);
1058+
command
1059+
}
1060+
10531061
/// Create a `uv export` command with options shared across scenarios.
10541062
pub fn export(&self) -> Command {
10551063
let mut command = Self::new_command();

crates/uv/tests/it/help.rs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn help() {
3333
venv Create a virtual environment
3434
build Build Python packages into source distributions and wheels
3535
publish Upload distributions to an index
36+
workspace Manage workspaces
3637
cache Manage uv's cache
3738
self Manage the uv executable
3839
generate-shell-completion Generate shell completion
@@ -98,26 +99,27 @@ fn help_flag() {
9899
Usage: uv [OPTIONS] <COMMAND>
99100
100101
Commands:
101-
auth Manage authentication
102-
run Run a command or script
103-
init Create a new project
104-
add Add dependencies to the project
105-
remove Remove dependencies from the project
106-
version Read or update the project's version
107-
sync Update the project's environment
108-
lock Update the project's lockfile
109-
export Export the project's lockfile to an alternate format
110-
tree Display the project's dependency tree
111-
format Format Python code in the project
112-
tool Run and install commands provided by Python packages
113-
python Manage Python versions and installations
114-
pip Manage Python packages with a pip-compatible interface
115-
venv Create a virtual environment
116-
build Build Python packages into source distributions and wheels
117-
publish Upload distributions to an index
118-
cache Manage uv's cache
119-
self Manage the uv executable
120-
help Display documentation for a command
102+
auth Manage authentication
103+
run Run a command or script
104+
init Create a new project
105+
add Add dependencies to the project
106+
remove Remove dependencies from the project
107+
version Read or update the project's version
108+
sync Update the project's environment
109+
lock Update the project's lockfile
110+
export Export the project's lockfile to an alternate format
111+
tree Display the project's dependency tree
112+
format Format Python code in the project
113+
tool Run and install commands provided by Python packages
114+
python Manage Python versions and installations
115+
pip Manage Python packages with a pip-compatible interface
116+
venv Create a virtual environment
117+
build Build Python packages into source distributions and wheels
118+
publish Upload distributions to an index
119+
workspace Manage workspaces
120+
cache Manage uv's cache
121+
self Manage the uv executable
122+
help Display documentation for a command
121123
122124
Cache options:
123125
-n, --no-cache Avoid reading from or writing to the cache, instead using a temporary
@@ -178,26 +180,27 @@ fn help_short_flag() {
178180
Usage: uv [OPTIONS] <COMMAND>
179181
180182
Commands:
181-
auth Manage authentication
182-
run Run a command or script
183-
init Create a new project
184-
add Add dependencies to the project
185-
remove Remove dependencies from the project
186-
version Read or update the project's version
187-
sync Update the project's environment
188-
lock Update the project's lockfile
189-
export Export the project's lockfile to an alternate format
190-
tree Display the project's dependency tree
191-
format Format Python code in the project
192-
tool Run and install commands provided by Python packages
193-
python Manage Python versions and installations
194-
pip Manage Python packages with a pip-compatible interface
195-
venv Create a virtual environment
196-
build Build Python packages into source distributions and wheels
197-
publish Upload distributions to an index
198-
cache Manage uv's cache
199-
self Manage the uv executable
200-
help Display documentation for a command
183+
auth Manage authentication
184+
run Run a command or script
185+
init Create a new project
186+
add Add dependencies to the project
187+
remove Remove dependencies from the project
188+
version Read or update the project's version
189+
sync Update the project's environment
190+
lock Update the project's lockfile
191+
export Export the project's lockfile to an alternate format
192+
tree Display the project's dependency tree
193+
format Format Python code in the project
194+
tool Run and install commands provided by Python packages
195+
python Manage Python versions and installations
196+
pip Manage Python packages with a pip-compatible interface
197+
venv Create a virtual environment
198+
build Build Python packages into source distributions and wheels
199+
publish Upload distributions to an index
200+
workspace Manage workspaces
201+
cache Manage uv's cache
202+
self Manage the uv executable
203+
help Display documentation for a command
201204
202205
Cache options:
203206
-n, --no-cache Avoid reading from or writing to the cache, instead using a temporary
@@ -892,6 +895,7 @@ fn help_unknown_subcommand() {
892895
venv
893896
build
894897
publish
898+
workspace
895899
cache
896900
self
897901
generate-shell-completion
@@ -921,6 +925,7 @@ fn help_unknown_subcommand() {
921925
venv
922926
build
923927
publish
928+
workspace
924929
cache
925930
self
926931
generate-shell-completion
@@ -979,6 +984,7 @@ fn help_with_global_option() {
979984
venv Create a virtual environment
980985
build Build Python packages into source distributions and wheels
981986
publish Upload distributions to an index
987+
workspace Manage workspaces
982988
cache Manage uv's cache
983989
self Manage the uv executable
984990
generate-shell-completion Generate shell completion
@@ -1102,6 +1108,7 @@ fn help_with_no_pager() {
11021108
venv Create a virtual environment
11031109
build Build Python packages into source distributions and wheels
11041110
publish Upload distributions to an index
1111+
workspace Manage workspaces
11051112
cache Manage uv's cache
11061113
self Manage the uv executable
11071114
generate-shell-completion Generate shell completion

crates/uv/tests/it/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,4 @@ mod workflow;
138138

139139
mod extract;
140140
mod workspace;
141+
mod workspace_metadata;

crates/uv/tests/it/show_settings.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7760,7 +7760,7 @@ fn preview_features() {
77607760
show_settings: true,
77617761
preview: Preview {
77627762
flags: PreviewFeatures(
7763-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
7763+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | WORKSPACE_METADATA,
77647764
),
77657765
},
77667766
python_preference: Managed,
@@ -7988,7 +7988,7 @@ fn preview_features() {
79887988
show_settings: true,
79897989
preview: Preview {
79907990
flags: PreviewFeatures(
7991-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
7991+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | WORKSPACE_METADATA,
79927992
),
79937993
},
79947994
python_preference: Managed,

0 commit comments

Comments
 (0)