Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions crates/ty/tests/cli/uv_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,86 @@ fn explicit_file_path_disables_uv_workspace_discovery() -> anyhow::Result<()> {
Ok(())
}

/// Scripts discovered under a selected directory are independent uv projects and must not be
/// analyzed as part of that directory's workspace member.
#[cfg(feature = "test-uv")]
#[test]
fn implicitly_discovered_scripts_are_not_checked() -> anyhow::Result<()> {
let case = workspace_case()?;
case.write_file(
"packages/member/script.py",
r#"
# /// script
# dependencies = []
# ///

value: int = "ignored-script"
"#,
)?;
case.write_file(
"packages/member/nested/script.py",
r#"
# /// script
# dependencies = []
# ///

value: int = "ignored-nested-script"
"#,
)?;

let mut command = command_with_uv(&case, None)?;
command
.current_dir(case.root().join("packages/member"))
.arg(".");

assert_cmd_snapshot!(command, @r#"
success: false
exit_code: 1
----- stdout -----
member.py:1:14: error[invalid-assignment] Object of type `Literal["selected-member"]` is not assignable to `int`
Found 1 diagnostic

----- stderr -----
"#);

Ok(())
}

/// Selecting a script itself explicitly opts into checking that script even in uv integration
/// mode.
#[cfg(feature = "test-uv")]
#[test]
fn explicitly_selected_script_is_checked() -> anyhow::Result<()> {
let case = workspace_case()?;
case.write_file(
"packages/member/script.py",
r#"
# /// script
# dependencies = []
# ///

value: int = "selected-script"
"#,
)?;

let mut command = command_with_uv(&case, None)?;
command
.current_dir(case.root().join("packages/member"))
.arg("script.py");

assert_cmd_snapshot!(command, @r#"
success: false
exit_code: 1
----- stdout -----
script.py:6:14: error[invalid-assignment] Object of type `Literal["selected-script"]` is not assignable to `int`
Found 1 diagnostic

----- stderr -----
"#);

Ok(())
}

/// An explicitly selected member inherits ty rule configuration from the uv workspace root.
#[cfg(feature = "test-uv")]
#[test]
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_project/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod configuration_file;
pub mod options;
pub mod pyproject;
pub mod python_version;
mod script;
pub(crate) mod script;
pub mod settings;
mod uv;
pub mod value;
Expand Down
14 changes: 14 additions & 0 deletions crates/ty_project/src/walk.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::glob::IncludeExcludeFilter;
use crate::metadata::script::script_metadata;
use crate::{Db, GlobFilterCheckMode, IncludeResult, Project};
use ruff_db::diagnostic::{Diagnostic, DiagnosticId, Severity};
use ruff_db::files::{File, system_path_to_file};
Expand Down Expand Up @@ -167,6 +168,7 @@ impl ProjectFilesWalker {
};

let filter = ProjectFilesFilter::from_project(db, project);
let skip_implicit_scripts = project.metadata(db).has_uv_workspace();
let files = std::sync::Mutex::new(Vec::new());
let diagnostics = std::sync::Mutex::new(Vec::new());

Expand Down Expand Up @@ -259,6 +261,18 @@ impl ProjectFilesWalker {
// If this returns `Err`, then the file was deleted between now and when the walk callback was called.
// We can ignore this.
if let Ok(file) = system_path_to_file(&*db, entry.path()) {
if entry.depth() > 0
&& skip_implicit_scripts
&& script_metadata(&*db, file).is_some()
{
tracing::debug!(
"Ignoring implicitly discovered PEP 723 script `{path}` \
while uv integration is enabled.",
path = entry.path()
);
return WalkState::Skip;
}

files.lock().unwrap().push(file);
}
}
Expand Down
Loading