diff --git a/crates/ruff_graph/src/db.rs b/crates/ruff_graph/src/db.rs index cbca766de6..6c5a6e8121 100644 --- a/crates/ruff_graph/src/db.rs +++ b/crates/ruff_graph/src/db.rs @@ -98,6 +98,10 @@ impl Db for ModuleDb { fn lint_registry(&self) -> &LintRegistry { default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db] diff --git a/crates/ty/src/lib.rs b/crates/ty/src/lib.rs index 6fb1b05232..e1bb4e1f10 100644 --- a/crates/ty/src/lib.rs +++ b/crates/ty/src/lib.rs @@ -14,7 +14,7 @@ use anyhow::Result; use std::sync::Mutex; use crate::args::{CheckCommand, Command, TerminalColor}; -use crate::logging::setup_tracing; +use crate::logging::{VerbosityLevel, setup_tracing}; use crate::printer::Printer; use anyhow::{Context, anyhow}; use clap::{CommandFactory, Parser}; @@ -128,6 +128,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result { let mut db = ProjectDatabase::new(project_metadata, system)?; + db.project() + .set_verbose(&mut db, verbosity >= VerbosityLevel::Verbose); if !check_paths.is_empty() { db.project().set_included_paths(&mut db, check_paths); } diff --git a/crates/ty/tests/cli/main.rs b/crates/ty/tests/cli/main.rs index 5270c1212c..45d7304be5 100644 --- a/crates/ty/tests/cli/main.rs +++ b/crates/ty/tests/cli/main.rs @@ -782,6 +782,8 @@ impl CliTest { let mut settings = insta::Settings::clone_current(); settings.add_filter(&tempdir_filter(&project_dir), "/"); settings.add_filter(r#"\\(\w\w|\s|\.|")"#, "/$1"); + // 0.003s + settings.add_filter(r"\d.\d\d\ds", "0.000s"); settings.add_filter( r#"The system cannot find the file specified."#, "No such file or directory", diff --git a/crates/ty/tests/cli/python_environment.rs b/crates/ty/tests/cli/python_environment.rs index a8a9f0038d..0a200c3c26 100644 --- a/crates/ty/tests/cli/python_environment.rs +++ b/crates/ty/tests/cli/python_environment.rs @@ -421,6 +421,94 @@ fn lib64_site_packages_directory_on_unix() -> anyhow::Result<()> { Ok(()) } +#[test] +fn many_search_paths() -> anyhow::Result<()> { + let case = CliTest::with_files([ + ("extra1/foo1.py", ""), + ("extra2/foo2.py", ""), + ("extra3/foo3.py", ""), + ("extra4/foo4.py", ""), + ("extra5/foo5.py", ""), + ("extra6/foo6.py", ""), + ("test.py", "import foo1, baz"), + ])?; + + assert_cmd_snapshot!( + case.command() + .arg("--python-platform").arg("linux") + .arg("--extra-search-path").arg("extra1") + .arg("--extra-search-path").arg("extra2") + .arg("--extra-search-path").arg("extra3") + .arg("--extra-search-path").arg("extra4") + .arg("--extra-search-path").arg("extra5") + .arg("--extra-search-path").arg("extra6"), + @r" + success: false + exit_code: 1 + ----- stdout ----- + error[unresolved-import]: Cannot resolve imported module `baz` + --> test.py:1:14 + | + 1 | import foo1, baz + | ^^^ + | + info: Searched in the following paths during module resolution: + info: 1. /extra1 (extra search path specified on the CLI or in your config file) + info: 2. /extra2 (extra search path specified on the CLI or in your config file) + info: 3. /extra3 (extra search path specified on the CLI or in your config file) + info: 4. /extra4 (extra search path specified on the CLI or in your config file) + info: 5. /extra5 (extra search path specified on the CLI or in your config file) + info: ... and 3 more paths. Run with `-v` to see all paths. + info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment + info: rule `unresolved-import` is enabled by default + + Found 1 diagnostic + + ----- stderr ----- + "); + + // Shows all with `-v` + assert_cmd_snapshot!( + case.command() + .arg("--python-platform").arg("linux") + .arg("--extra-search-path").arg("extra1") + .arg("--extra-search-path").arg("extra2") + .arg("--extra-search-path").arg("extra3") + .arg("--extra-search-path").arg("extra4") + .arg("--extra-search-path").arg("extra5") + .arg("--extra-search-path").arg("extra6") + .arg("-v"), + @r" + success: false + exit_code: 1 + ----- stdout ----- + error[unresolved-import]: Cannot resolve imported module `baz` + --> test.py:1:14 + | + 1 | import foo1, baz + | ^^^ + | + info: Searched in the following paths during module resolution: + info: 1. /extra1 (extra search path specified on the CLI or in your config file) + info: 2. /extra2 (extra search path specified on the CLI or in your config file) + info: 3. /extra3 (extra search path specified on the CLI or in your config file) + info: 4. /extra4 (extra search path specified on the CLI or in your config file) + info: 5. /extra5 (extra search path specified on the CLI or in your config file) + info: 6. /extra6 (extra search path specified on the CLI or in your config file) + info: 7. / (first-party code) + info: 8. vendored://stdlib (stdlib typeshed stubs vendored by ty) + info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment + info: rule `unresolved-import` is enabled by default + + Found 1 diagnostic + + ----- stderr ----- + INFO Python version: Python 3.14, platform: linux + INFO Indexed 7 file(s) in 0.000s + "); + Ok(()) +} + #[test] fn pyvenv_cfg_file_annotation_showing_where_python_version_set() -> anyhow::Result<()> { let case = CliTest::with_files([ diff --git a/crates/ty_project/src/db.rs b/crates/ty_project/src/db.rs index a312586fb6..5e2105839e 100644 --- a/crates/ty_project/src/db.rs +++ b/crates/ty_project/src/db.rs @@ -457,6 +457,10 @@ impl SemanticDb for ProjectDatabase { fn lint_registry(&self) -> &LintRegistry { ty_python_semantic::default_lint_registry() } + + fn verbose(&self) -> bool { + self.project().verbose(self) + } } #[salsa::db] @@ -609,6 +613,10 @@ pub(crate) mod tests { fn lint_registry(&self) -> &LintRegistry { ty_python_semantic::default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db] diff --git a/crates/ty_project/src/lib.rs b/crates/ty_project/src/lib.rs index f5ef2201da..d47476c7dc 100644 --- a/crates/ty_project/src/lib.rs +++ b/crates/ty_project/src/lib.rs @@ -113,6 +113,9 @@ pub struct Project { /// the project including the virtual files that might exists in the editor. #[default] check_mode: CheckMode, + + #[default] + verbose_flag: bool, } /// A progress reporter. @@ -368,6 +371,16 @@ impl Project { self.reload_files(db); } + pub fn set_verbose(self, db: &mut dyn Db, verbose: bool) { + if self.verbose_flag(db) != verbose { + self.set_verbose_flag(db).to(verbose); + } + } + + pub fn verbose(self, db: &dyn Db) -> bool { + self.verbose_flag(db) + } + /// Returns the paths that should be checked. /// /// The default is to check the entire project in which case this method returns diff --git a/crates/ty_python_semantic/src/db.rs b/crates/ty_python_semantic/src/db.rs index 645929235a..d03b001199 100644 --- a/crates/ty_python_semantic/src/db.rs +++ b/crates/ty_python_semantic/src/db.rs @@ -12,6 +12,9 @@ pub trait Db: SourceDb { fn rule_selection(&self, file: File) -> &RuleSelection; fn lint_registry(&self) -> &LintRegistry; + + /// Whether ty is running with logging verbosity INFO or higher (`-v` or more). + fn verbose(&self) -> bool; } #[cfg(test)] @@ -126,6 +129,10 @@ pub(crate) mod tests { fn lint_registry(&self) -> &LintRegistry { default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db] diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index be92acc9ab..7155e1f7a1 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -4811,22 +4811,29 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // Add search paths information to the diagnostic // Use the same search paths function that is used in actual module resolution - let mut search_paths = - search_paths(self.db(), ModuleResolveMode::StubsAllowed).peekable(); + let verbose = self.db().verbose(); + let search_paths = search_paths(self.db(), ModuleResolveMode::StubsAllowed); - if search_paths.peek().is_some() { - diagnostic.info(format_args!( - "Searched in the following paths during module resolution:" - )); + diagnostic.info(format_args!( + "Searched in the following paths during module resolution:" + )); - for (index, path) in search_paths.enumerate() { + let mut search_paths = search_paths.enumerate(); + + while let Some((index, path)) = search_paths.next() { + if index > 4 && !verbose { + let more = search_paths.count() + 1; diagnostic.info(format_args!( - " {}. {} ({})", - index + 1, - path, - path.describe_kind() + " ... and {more} more paths. Run with `-v` to see all paths." )); + break; } + diagnostic.info(format_args!( + " {}. {} ({})", + index + 1, + path, + path.describe_kind() + )); } diagnostic.info( diff --git a/crates/ty_python_semantic/tests/corpus.rs b/crates/ty_python_semantic/tests/corpus.rs index 83ad7ae1ff..db2b1f3342 100644 --- a/crates/ty_python_semantic/tests/corpus.rs +++ b/crates/ty_python_semantic/tests/corpus.rs @@ -255,6 +255,10 @@ impl ty_python_semantic::Db for CorpusDb { fn lint_registry(&self) -> &LintRegistry { default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db] diff --git a/crates/ty_test/src/db.rs b/crates/ty_test/src/db.rs index 100b26b5fb..cb05fa0bd8 100644 --- a/crates/ty_test/src/db.rs +++ b/crates/ty_test/src/db.rs @@ -88,6 +88,10 @@ impl SemanticDb for Db { fn lint_registry(&self) -> &LintRegistry { default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db] diff --git a/fuzz/fuzz_targets/ty_check_invalid_syntax.rs b/fuzz/fuzz_targets/ty_check_invalid_syntax.rs index 9a6c6eb1f6..8bf19e88f7 100644 --- a/fuzz/fuzz_targets/ty_check_invalid_syntax.rs +++ b/fuzz/fuzz_targets/ty_check_invalid_syntax.rs @@ -93,6 +93,10 @@ impl SemanticDb for TestDb { fn lint_registry(&self) -> &LintRegistry { default_lint_registry() } + + fn verbose(&self) -> bool { + false + } } #[salsa::db]