Skip to content

Commit ce020ef

Browse files
AryanBagademeta-codesync[bot]
authored andcommitted
Support specifying baseline in configuration file (#2137)
Summary: Adds support for setting the baseline file path in `pyrefly.toml` or `pyproject.toml`, so users don't need to pass `--baseline` on every invocation. ```toml # pyrefly.toml baseline = "baseline.json" ``` - Add baseline field to ConfigFile struct - Read baseline from config when CLI flag not provided - CLI --baseline takes precedence over config - Update documentation Fixes #2119 Pull Request resolved: #2137 Test Plan: - Added unit test for parsing baseline from config - Updated test_rewrite_with_path_to_config to include baseline path - Ran python3 test.py -> all tests pass - Manual testing: verified baseline works from config, CLI override works Reviewed By: yangdanny97 Differential Revision: D90895437 Pulled By: grievejia fbshipit-source-id: 1117c281fbbf4ba9c96c53bddaa2e12e64e7bce5
1 parent c6ca82e commit ce020ef

File tree

3 files changed

+74
-8
lines changed

3 files changed

+74
-8
lines changed

crates/pyrefly_config/src/config.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ pub struct ConfigFile {
465465
#[serde(default, skip_serializing_if = "Option::is_none")]
466466
pub typeshed_path: Option<PathBuf>,
467467

468+
/// Path to baseline file for comparing type errors.
469+
/// Errors matching the baseline are suppressed.
470+
#[serde(default, skip_serializing_if = "Option::is_none")]
471+
pub baseline: Option<PathBuf>,
472+
468473
/// Pyrefly's configurations around interpreter querying/finding.
469474
#[serde(flatten)]
470475
pub interpreters: Interpreters,
@@ -542,6 +547,7 @@ impl Default for ConfigFile {
542547
source_db: Default::default(),
543548
use_ignore_files: true,
544549
typeshed_path: None,
550+
baseline: None,
545551
skip_lsp_config_indexing: false,
546552
}
547553
}
@@ -1176,6 +1182,9 @@ impl ConfigFile {
11761182
if let Some(typeshed_path) = &self.typeshed_path {
11771183
self.typeshed_path = Some(typeshed_path.absolutize_from(config_root));
11781184
}
1185+
if let Some(baseline) = &self.baseline {
1186+
self.baseline = Some(baseline.absolutize_from(config_root));
1187+
}
11791188
self.python_environment
11801189
.site_package_path
11811190
.iter_mut()
@@ -1441,6 +1450,7 @@ mod tests {
14411450
}
14421451
}],
14431452
typeshed_path: None,
1453+
baseline: None,
14441454
skip_lsp_config_indexing: false,
14451455
}
14461456
);
@@ -1673,6 +1683,7 @@ mod tests {
16731683
settings: Default::default(),
16741684
}],
16751685
typeshed_path: Some(PathBuf::from(typeshed)),
1686+
baseline: Some(PathBuf::from("baseline.json")),
16761687
skip_lsp_config_indexing: false,
16771688
};
16781689

@@ -1731,6 +1742,7 @@ mod tests {
17311742
settings: Default::default(),
17321743
}],
17331744
typeshed_path: Some(expected_typeshed),
1745+
baseline: Some(test_path.join("baseline.json")),
17341746
skip_lsp_config_indexing: false,
17351747
};
17361748
assert_eq!(config, expected_config);
@@ -1758,6 +1770,15 @@ mod tests {
17581770
assert!(err.to_string().contains("missing field `matches`"));
17591771
}
17601772

1773+
#[test]
1774+
fn test_baseline_config_parsing() {
1775+
let config_str = r#"
1776+
baseline = "baseline.json"
1777+
"#;
1778+
let config = ConfigFile::parse_config(config_str).unwrap();
1779+
assert_eq!(config.baseline, Some(PathBuf::from("baseline.json")));
1780+
}
1781+
17611782
#[test]
17621783
fn test_expect_all_fields_set_in_root_config() {
17631784
let root = TempDir::new().unwrap();

pyrefly/lib/commands/check.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ impl Timings {
650650

651651
impl CheckArgs {
652652
pub fn run_once(
653-
self,
653+
mut self,
654654
files_to_check: Box<dyn Includes>,
655655
config_finder: ConfigFinder,
656656
) -> anyhow::Result<(CommandExitStatus, Vec<Error>)> {
@@ -677,6 +677,18 @@ impl CheckArgs {
677677
true,
678678
);
679679
let (loaded_handles, _, sourcedb_errors) = handles.all(holder.as_ref().config_finder());
680+
681+
// If CLI doesn't provide baseline, get from config
682+
if self.output.baseline.is_none()
683+
&& let Some(handle) = loaded_handles.first()
684+
{
685+
let config = holder.as_ref().config_finder().python_file(
686+
ModuleNameWithKind::guaranteed(handle.module()),
687+
handle.path(),
688+
);
689+
self.output.baseline = config.baseline.clone();
690+
}
691+
680692
self.run_inner(
681693
timings,
682694
transaction.as_mut(),
@@ -687,7 +699,7 @@ impl CheckArgs {
687699
}
688700

689701
pub fn run_once_with_snippet(
690-
self,
702+
mut self,
691703
code: String,
692704
config_finder: ConfigFinder,
693705
) -> anyhow::Result<(CommandExitStatus, Vec<Error>)> {
@@ -699,13 +711,18 @@ impl CheckArgs {
699711
let holder = Forgetter::new(State::new(config_finder), true);
700712

701713
// Create a single handle for the virtual module
702-
let sys_info = holder
714+
let config = holder
703715
.as_ref()
704716
.config_finder()
705-
.python_file(ModuleNameWithKind::guaranteed(module_name), &module_path)
706-
.get_sys_info();
717+
.python_file(ModuleNameWithKind::guaranteed(module_name), &module_path);
718+
let sys_info = config.get_sys_info();
707719
let handle = Handle::new(module_name, module_path.clone(), sys_info);
708720

721+
// If CLI doesn't provide baseline, get from config
722+
if self.output.baseline.is_none() {
723+
self.output.baseline = config.baseline.clone();
724+
}
725+
709726
let require_levels = self.get_required_levels();
710727
let mut transaction = Forgetter::new(
711728
holder
@@ -730,7 +747,7 @@ impl CheckArgs {
730747
}
731748

732749
pub async fn run_watch(
733-
self,
750+
mut self,
734751
mut watcher: Watcher,
735752
files_to_check: Box<dyn Includes>,
736753
config_finder: ConfigFinder,
@@ -741,6 +758,19 @@ impl CheckArgs {
741758
let require_levels = self.get_required_levels();
742759
let mut handles = Handles::new(expanded_file_list);
743760
let state = State::new(config_finder);
761+
762+
// If CLI doesn't provide baseline, get from config
763+
if self.output.baseline.is_none() {
764+
let (loaded_handles, _, _) = handles.all(state.config_finder());
765+
if let Some(handle) = loaded_handles.first() {
766+
let config = state.config_finder().python_file(
767+
ModuleNameWithKind::guaranteed(handle.module()),
768+
handle.path(),
769+
);
770+
self.output.baseline = config.baseline.clone();
771+
}
772+
}
773+
744774
let mut transaction = state.new_committable_transaction(require_levels.default, None);
745775
loop {
746776
let timings = Timings::new();

website/docs/error-suppressions.mdx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,29 @@ To generate (or re-generate) the baseline file:
8686
pyrefly check --baseline="<path to baseline file>" --update-baseline
8787
```
8888

89-
To check your project using a baseline file and report only newly-introduced errors:
89+
To check your project using a baseline file and report only newly-introduced errors, you can either use the CLI flag:
9090

9191
```
9292
pyrefly check --baseline="<path to baseline file>"
9393
```
9494

95+
Or specify the baseline in your configuration file (`pyrefly.toml` or `pyproject.toml`):
96+
97+
```toml
98+
# pyrefly.toml
99+
baseline = "baseline.json"
100+
```
101+
102+
```toml
103+
# pyproject.toml
104+
[tool.pyrefly]
105+
baseline = "baseline.json"
106+
```
107+
108+
When the baseline is specified in the configuration file, you don't need to pass the `--baseline` flag on every invocation. The CLI flag takes precedence if both are specified.
109+
95110
Errors are matched with the baseline by looking at file, error code, and column number.
96-
Right now, errors suppressed by the baseline file are still shown in the IDE and the baseline file can only be passed as a command line argument, but both are expected to change in the future.
111+
Note that errors suppressed by the baseline file are still shown in the IDE.
97112
This feature is experimental, so please submit any feedback or requests you have on our Github repo.
98113

99114
## Upgrading Pyrefly (And other changes that introduce new type errors)

0 commit comments

Comments
 (0)