Skip to content
Merged
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
1 change: 1 addition & 0 deletions crates/ty/docs/cli.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions crates/ty/docs/configuration.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions crates/ty/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,19 @@ pub(crate) struct CheckCommand {
#[clap(long, overrides_with("force_exclude"), hide = true)]
no_force_exclude: bool,

/// Exclude files containing PEP 723 inline script metadata unless passed explicitly.
/// Use `--include-scripts` to disable.
#[arg(
long,
overrides_with("include_scripts"),
help_heading = "File selection",
default_missing_value = "true",
num_args = 0..1
)]
exclude_scripts: Option<bool>,
#[clap(long, overrides_with("exclude_scripts"), hide = true)]
include_scripts: bool,

/// Glob patterns for files to exclude from type checking.
///
/// Uses gitignore-style syntax to exclude files and directories from type checking.
Expand Down Expand Up @@ -251,6 +264,10 @@ impl CheckCommand {
.no_respect_ignore_files
.then_some(false)
.or(self.respect_ignore_files);
let exclude_scripts = self
.include_scripts
.then_some(false)
.or(self.exclude_scripts);
let error_on_warning = self
.exit_zero_on_warning
.then_some(false)
Expand Down Expand Up @@ -279,6 +296,7 @@ impl CheckCommand {
}),
src: Some(SrcOptions {
respect_ignore_files,
exclude_scripts,
exclude: self.exclude.map(|excludes| {
RangedValue::cli(excludes.iter().map(RelativeGlobPattern::cli).collect())
}),
Expand Down
88 changes: 88 additions & 0 deletions crates/ty/tests/cli/file_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,94 @@ use insta_cmd::assert_cmd_snapshot;

use crate::CliTest;

#[test]
fn exclude_scripts_only_applies_to_implicitly_discovered_files() -> anyhow::Result<()> {
let case = CliTest::with_files([
("main.py", "value: int = 'project'"),
(
"script.py",
r#"
# /// script
# dependencies = []
# ///
value: int = "script"
"#,
),
(
"nested/script.py",
r#"
# /// script
# dependencies = []
# ///
value: int = "nested-script"
"#,
),
])?;

assert_cmd_snapshot!(case.command().env("TY_OUTPUT_FORMAT", "concise"), @r#"
success: false
exit_code: 1
----- stdout -----
main.py:1:14: error[invalid-assignment] Object of type `Literal["project"]` is not assignable to `int`
nested/script.py:5:14: error[invalid-assignment] Object of type `Literal["nested-script"]` is not assignable to `int`
script.py:5:14: error[invalid-assignment] Object of type `Literal["script"]` is not assignable to `int`
Found 3 diagnostics

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

assert_cmd_snapshot!(case.command().env("TY_OUTPUT_FORMAT", "concise").arg("--exclude-scripts"), @r#"
success: false
exit_code: 1
----- stdout -----
main.py:1:14: error[invalid-assignment] Object of type `Literal["project"]` is not assignable to `int`
Found 1 diagnostic

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

assert_cmd_snapshot!(case.command().env("TY_OUTPUT_FORMAT", "concise").arg("--exclude-scripts").arg("script.py"), @r#"
success: false
exit_code: 1
----- stdout -----
script.py:5:14: error[invalid-assignment] Object of type `Literal["script"]` is not assignable to `int`
Found 1 diagnostic

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

case.write_file(
"ty.toml",
r#"
[src]
exclude-scripts = true
"#,
)?;

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

----- stderr -----
"#);
assert_cmd_snapshot!(case.command().env("TY_OUTPUT_FORMAT", "concise").arg("--include-scripts"), @r#"
success: false
exit_code: 1
----- stdout -----
main.py:1:14: error[invalid-assignment] Object of type `Literal["project"]` is not assignable to `int`
nested/script.py:5:14: error[invalid-assignment] Object of type `Literal["nested-script"]` is not assignable to `int`
script.py:5:14: error[invalid-assignment] Object of type `Literal["script"]` is not assignable to `int`
Found 3 diagnostics

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

Ok(())
}

/// Test exclude CLI argument functionality
#[test]
fn exclude_argument() -> anyhow::Result<()> {
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
13 changes: 13 additions & 0 deletions crates/ty_project/src/metadata/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,18 @@ pub struct SrcOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub respect_ignore_files: Option<bool>,

/// Whether to exclude files containing PEP 723 inline script metadata unless they are
/// explicitly passed on the command line.
#[option(
default = r#"false"#,
value_type = r#"bool"#,
example = r#"
exclude-scripts = true
"#
)]
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude_scripts: Option<bool>,

/// A list of files and directories to check. The `include` option
/// follows a similar syntax to `.gitignore` but reversed:
/// Including a file or directory will make it so that it (and its contents)
Expand Down Expand Up @@ -1062,6 +1074,7 @@ impl SrcOptions {

Ok(SrcSettings {
respect_ignore_files: self.respect_ignore_files.unwrap_or(true),
exclude_scripts: self.exclude_scripts.unwrap_or(false),
files,
})
}
Expand Down
2 changes: 2 additions & 0 deletions crates/ty_project/src/metadata/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,14 @@ impl Default for TerminalSettings {
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
pub struct SrcSettings {
pub respect_ignore_files: bool,
pub exclude_scripts: bool,
pub files: IncludeExcludeFilter,
}
impl SrcSettings {
pub(crate) fn default() -> Self {
Self {
respect_ignore_files: true,
exclude_scripts: false,
files: IncludeExcludeFilter::default(),
}
}
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 exclude_scripts = project.settings(db).src().exclude_scripts;
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
&& exclude_scripts
&& script_metadata(&*db, file).is_some()
{
tracing::debug!(
"Ignoring implicitly discovered PEP 723 script `{path}` \
because `exclude-scripts` is enabled.",
path = entry.path()
);
return WalkState::Skip;
}

files.lock().unwrap().push(file);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Settings: Settings {
},
src: SrcSettings {
respect_ignore_files: true,
exclude_scripts: false,
files: IncludeExcludeFilter {
include: IncludeFilter(
[
Expand Down
7 changes: 7 additions & 0 deletions ty.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading