From 06e7c29e57be46ecc2163bc2b807731e81b73b87 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 18 Nov 2025 09:40:08 +0100 Subject: [PATCH 1/5] [ty] Exit with `2` if there's any IO error --- crates/ty/src/lib.rs | 67 ++++++++++++++++++++++++++----------- crates/ty/tests/cli/main.rs | 6 ++-- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/crates/ty/src/lib.rs b/crates/ty/src/lib.rs index 9c667b0c826d60..e93292936c684c 100644 --- a/crates/ty/src/lib.rs +++ b/crates/ty/src/lib.rs @@ -5,6 +5,7 @@ mod python_version; mod version; pub use args::Cli; +use ty_project::metadata::settings::TerminalSettings; use ty_static::EnvVars; use std::fmt::Write; @@ -21,7 +22,9 @@ use clap::{CommandFactory, Parser}; use colored::Colorize; use crossbeam::channel as crossbeam_channel; use rayon::ThreadPoolBuilder; -use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, DisplayDiagnostics, Severity}; +use ruff_db::diagnostic::{ + Diagnostic, DiagnosticId, DisplayDiagnosticConfig, DisplayDiagnostics, Severity, +}; use ruff_db::files::File; use ruff_db::max_parallelism; use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf}; @@ -193,6 +196,12 @@ pub enum ExitStatus { InternalError = 101, } +impl ExitStatus { + pub const fn is_internal_error(self) -> bool { + matches!(self, ExitStatus::InternalError) + } +} + impl Termination for ExitStatus { fn report(self) -> ExitCode { ExitCode::from(self as u8) @@ -334,11 +343,8 @@ impl MainLoop { let diagnostics_count = result.len(); let mut stdout = self.printer.stream_for_details().lock(); - let max_severity = result - .iter() - .map(Diagnostic::severity) - .max() - .unwrap_or(Severity::Info); + let exit_status = + exit_status_from_diagnostics(&result, terminal_settings); // Only render diagnostics if they're going to be displayed, since doing // so is expensive. @@ -359,25 +365,14 @@ impl MainLoop { )?; } - if max_severity.is_fatal() { + if exit_status.is_internal_error() { tracing::warn!( "A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details." ); } if self.watcher.is_none() { - return Ok(match max_severity { - Severity::Info => ExitStatus::Success, - Severity::Warning => { - if terminal_settings.error_on_warning { - ExitStatus::Failure - } else { - ExitStatus::Success - } - } - Severity::Error => ExitStatus::Failure, - Severity::Fatal => ExitStatus::InternalError, - }); + return Ok(exit_status); } } } else { @@ -410,6 +405,40 @@ impl MainLoop { } } +fn exit_status_from_diagnostics( + diagnostics: &[Diagnostic], + terminal_settings: &TerminalSettings, +) -> ExitStatus { + if diagnostics.is_empty() { + return ExitStatus::Success; + } + + let mut max_severity = Severity::Info; + let mut io_error = false; + + for diagnostic in diagnostics { + max_severity = max_severity.max(diagnostic.severity()); + io_error = io_error || matches!(diagnostic.id(), DiagnosticId::Io); + } + + if !max_severity.is_fatal() && io_error { + return ExitStatus::Error; + } + + match max_severity { + Severity::Info => ExitStatus::Success, + Severity::Warning => { + if terminal_settings.error_on_warning { + ExitStatus::Failure + } else { + ExitStatus::Success + } + } + Severity::Error => ExitStatus::Failure, + Severity::Fatal => ExitStatus::InternalError, + } +} + /// A progress reporter for `ty check`. struct IndicatifReporter { collector: CollectReporter, diff --git a/crates/ty/tests/cli/main.rs b/crates/ty/tests/cli/main.rs index 6640c4f598dc71..a352ba355a2c08 100644 --- a/crates/ty/tests/cli/main.rs +++ b/crates/ty/tests/cli/main.rs @@ -562,9 +562,9 @@ fn check_non_existing_path() -> anyhow::Result<()> { assert_cmd_snapshot!( case.command().arg("project/main.py").arg("project/tests"), - @r###" + @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- error[io]: `/project/main.py`: No such file or directory (os error 2) @@ -574,7 +574,7 @@ fn check_non_existing_path() -> anyhow::Result<()> { ----- stderr ----- WARN No python files found under the given path(s) - "### + " ); Ok(()) From 1dd5f7236cb5e76c708b21355bf211c7e3e10164 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 18 Nov 2025 09:50:38 +0100 Subject: [PATCH 2/5] Exclude `Pythonwin/pywin/test/_dbgscript.py` --- .github/mypy-primer-ty.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/mypy-primer-ty.toml b/.github/mypy-primer-ty.toml index 9317364fe263f9..5aa9c4e66a8c96 100644 --- a/.github/mypy-primer-ty.toml +++ b/.github/mypy-primer-ty.toml @@ -6,3 +6,8 @@ possibly-unresolved-reference = "warn" unused-ignore-comment = "warn" division-by-zero = "warn" + +[src] +exclude = [ + "Pythonwin/pywin/test/_dbgscript.py" # not valid UTF-8 +] From 9f1181ef30730e4dc2deb50c0d87b1dff1bf2283 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 18 Nov 2025 09:57:55 +0100 Subject: [PATCH 3/5] More exceptions --- .github/mypy-primer-ty.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/mypy-primer-ty.toml b/.github/mypy-primer-ty.toml index 5aa9c4e66a8c96..834ea043377a0b 100644 --- a/.github/mypy-primer-ty.toml +++ b/.github/mypy-primer-ty.toml @@ -9,5 +9,8 @@ division-by-zero = "warn" [src] exclude = [ - "Pythonwin/pywin/test/_dbgscript.py" # not valid UTF-8 + # not valid UTF-8 + "Pythonwin/pywin/test/_dbgscript.py", + "tests/appsec/iast/fixtures/aspects/str/non_utf8_content.py", + "tests/appsec/iast/fixtures/ast/str/non_utf8_content.py", ] From 43f165d78749baa754e464ecd50a3480143dabba Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 18 Nov 2025 10:13:06 +0100 Subject: [PATCH 4/5] Use new mypy primer --- .github/mypy-primer-ty.toml | 8 -------- scripts/mypy_primer.sh | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/mypy-primer-ty.toml b/.github/mypy-primer-ty.toml index 834ea043377a0b..9317364fe263f9 100644 --- a/.github/mypy-primer-ty.toml +++ b/.github/mypy-primer-ty.toml @@ -6,11 +6,3 @@ possibly-unresolved-reference = "warn" unused-ignore-comment = "warn" division-by-zero = "warn" - -[src] -exclude = [ - # not valid UTF-8 - "Pythonwin/pywin/test/_dbgscript.py", - "tests/appsec/iast/fixtures/aspects/str/non_utf8_content.py", - "tests/appsec/iast/fixtures/ast/str/non_utf8_content.py", -] diff --git a/scripts/mypy_primer.sh b/scripts/mypy_primer.sh index c140721745d716..cdb08bfa69b72e 100755 --- a/scripts/mypy_primer.sh +++ b/scripts/mypy_primer.sh @@ -20,7 +20,7 @@ cd .. echo "Project selector: ${PRIMER_SELECTOR}" # Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs uvx \ - --from="git+https://github.com/hauntsaninja/mypy_primer@ab5d30e2d4ecdaf7d6cc89395c7130143d6d3c82" \ + --from="git+https://github.com/hauntsaninja/mypy_primer@089ac1da83cf26aee9c98de412b7eb10e20b2212" \ mypy_primer \ --repo ruff \ --type-checker ty \ From 8ae61d31584507f0550e980f288f95ce76826f06 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 18 Nov 2025 19:41:10 +0100 Subject: [PATCH 5/5] Update ecosystem-analyzer --- .github/workflows/ty-ecosystem-analyzer.yaml | 2 +- .github/workflows/ty-ecosystem-report.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ty-ecosystem-analyzer.yaml b/.github/workflows/ty-ecosystem-analyzer.yaml index a1640e59740cf8..a20018ea7518c2 100644 --- a/.github/workflows/ty-ecosystem-analyzer.yaml +++ b/.github/workflows/ty-ecosystem-analyzer.yaml @@ -67,7 +67,7 @@ jobs: cd .. - uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@0aff03414da5d242e97a9f43fb502e085637a4a1" + uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e5c5f5b2d762af91b28490537fe0077334165693" ecosystem-analyzer \ --repository ruff \ diff --git a/.github/workflows/ty-ecosystem-report.yaml b/.github/workflows/ty-ecosystem-report.yaml index c9df4f1b28f8a6..904d19611d7027 100644 --- a/.github/workflows/ty-ecosystem-report.yaml +++ b/.github/workflows/ty-ecosystem-report.yaml @@ -52,7 +52,7 @@ jobs: cd .. - uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@0aff03414da5d242e97a9f43fb502e085637a4a1" + uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@e5c5f5b2d762af91b28490537fe0077334165693" ecosystem-analyzer \ --verbose \