Skip to content

Commit ab82b51

Browse files
committed
Add uv workspace metadata
1 parent 9a21897 commit ab82b51

File tree

12 files changed

+576
-43
lines changed

12 files changed

+576
-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
@@ -6835,6 +6841,21 @@ pub struct PublishArgs {
68356841
pub dry_run: bool,
68366842
}
68376843

6844+
#[derive(Args)]
6845+
pub struct WorkspaceNamespace {
6846+
#[command(subcommand)]
6847+
pub command: WorkspaceCommand,
6848+
}
6849+
6850+
#[derive(Subcommand)]
6851+
pub enum WorkspaceCommand {
6852+
/// Display package metadata.
6853+
Metadata(MetadataArgs),
6854+
}
6855+
6856+
#[derive(Args, Debug)]
6857+
pub struct MetadataArgs;
6858+
68386859
/// See [PEP 517](https://peps.python.org/pep-0517/) and
68396860
/// [PEP 660](https://peps.python.org/pep-0660/) for specifications of the parameters.
68406861
#[derive(Subcommand)]

crates/uv-preview/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ bitflags::bitflags! {
2121
const NATIVE_AUTH = 1 << 9;
2222
const S3_ENDPOINT = 1 << 10;
2323
const CACHE_SIZE = 1 << 11;
24+
const WORKSPACE_METADATA = 1 << 12;
2425
}
2526
}
2627

@@ -42,6 +43,7 @@ impl PreviewFeatures {
4243
Self::NATIVE_AUTH => "native-auth",
4344
Self::S3_ENDPOINT => "s3-endpoint",
4445
Self::CACHE_SIZE => "cache-size",
46+
Self::WORKSPACE_METADATA => "workspace-metadata",
4547
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
4648
}
4749
}
@@ -91,6 +93,7 @@ impl FromStr for PreviewFeatures {
9193
"native-auth" => Self::NATIVE_AUTH,
9294
"s3-endpoint" => Self::S3_ENDPOINT,
9395
"cache-size" => Self::CACHE_SIZE,
96+
"workspace-metadata" => Self::WORKSPACE_METADATA,
9497
_ => {
9598
warn_user_once!("Unknown preview feature: `{part}`");
9699
continue;

crates/uv/src/commands/mod.rs

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

7172
use crate::printer::Printer;
7273

@@ -88,6 +89,7 @@ pub(crate) mod reporters;
8889
mod self_update;
8990
mod tool;
9091
mod venv;
92+
mod workspace;
9193

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

1067+
/// Create a `uv workspace metadata` command with options shared across scenarios.
1068+
pub fn workspace_metadata(&self) -> Command {
1069+
let mut command = Self::new_command();
1070+
command.arg("workspace").arg("metadata");
1071+
self.add_shared_options(&mut command, false);
1072+
command
1073+
}
1074+
10671075
/// Create a `uv export` command with options shared across scenarios.
10681076
pub fn export(&self) -> Command {
10691077
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
@@ -141,3 +141,4 @@ mod workflow;
141141

142142
mod extract;
143143
mod workspace;
144+
mod workspace_metadata;

crates/uv/tests/it/show_settings.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7831,7 +7831,7 @@ fn preview_features() {
78317831
show_settings: true,
78327832
preview: Preview {
78337833
flags: PreviewFeatures(
7834-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
7834+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | WORKSPACE_METADATA,
78357835
),
78367836
},
78377837
python_preference: Managed,
@@ -8059,7 +8059,7 @@ fn preview_features() {
80598059
show_settings: true,
80608060
preview: Preview {
80618061
flags: PreviewFeatures(
8062-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
8062+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | WORKSPACE_METADATA,
80638063
),
80648064
},
80658065
python_preference: Managed,

0 commit comments

Comments
 (0)