Skip to content

Commit dce87c2

Browse files
authored
Eagerly validate typeshed versions (#12786)
1 parent f873d2a commit dce87c2

File tree

31 files changed

+679
-501
lines changed

31 files changed

+679
-501
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/red_knot/src/main.rs

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ use colored::Colorize;
77
use crossbeam::channel as crossbeam_channel;
88
use salsa::plumbing::ZalsaDatabase;
99

10-
use red_knot_python_semantic::{ProgramSettings, SearchPathSettings};
10+
use red_knot_python_semantic::SitePackages;
1111
use red_knot_server::run_server;
1212
use red_knot_workspace::db::RootDatabase;
13-
use red_knot_workspace::site_packages::VirtualEnvironment;
1413
use red_knot_workspace::watch;
1514
use red_knot_workspace::watch::WorkspaceWatcher;
15+
use red_knot_workspace::workspace::settings::Configuration;
1616
use red_knot_workspace::workspace::WorkspaceMetadata;
1717
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
1818
use target_version::TargetVersion;
@@ -65,15 +65,14 @@ to resolve type information for the project's third-party dependencies.",
6565
value_name = "PATH",
6666
help = "Additional path to use as a module-resolution source (can be passed multiple times)"
6767
)]
68-
extra_search_path: Vec<SystemPathBuf>,
68+
extra_search_path: Option<Vec<SystemPathBuf>>,
6969

7070
#[arg(
7171
long,
7272
help = "Python version to assume when resolving types",
73-
default_value_t = TargetVersion::default(),
74-
value_name="VERSION")
75-
]
76-
target_version: TargetVersion,
73+
value_name = "VERSION"
74+
)]
75+
target_version: Option<TargetVersion>,
7776

7877
#[clap(flatten)]
7978
verbosity: Verbosity,
@@ -86,6 +85,36 @@ to resolve type information for the project's third-party dependencies.",
8685
watch: bool,
8786
}
8887

88+
impl Args {
89+
fn to_configuration(&self, cli_cwd: &SystemPath) -> Configuration {
90+
let mut configuration = Configuration::default();
91+
92+
if let Some(target_version) = self.target_version {
93+
configuration.target_version = Some(target_version.into());
94+
}
95+
96+
if let Some(venv_path) = &self.venv_path {
97+
configuration.search_paths.site_packages = Some(SitePackages::Derived {
98+
venv_path: SystemPath::absolute(venv_path, cli_cwd),
99+
});
100+
}
101+
102+
if let Some(custom_typeshed_dir) = &self.custom_typeshed_dir {
103+
configuration.search_paths.custom_typeshed =
104+
Some(SystemPath::absolute(custom_typeshed_dir, cli_cwd));
105+
}
106+
107+
if let Some(extra_search_paths) = &self.extra_search_path {
108+
configuration.search_paths.extra_paths = extra_search_paths
109+
.iter()
110+
.map(|path| Some(SystemPath::absolute(path, cli_cwd)))
111+
.collect();
112+
}
113+
114+
configuration
115+
}
116+
}
117+
89118
#[derive(Debug, clap::Subcommand)]
90119
pub enum Command {
91120
/// Start the language server
@@ -115,22 +144,13 @@ pub fn main() -> ExitStatus {
115144
}
116145

117146
fn run() -> anyhow::Result<ExitStatus> {
118-
let Args {
119-
command,
120-
current_directory,
121-
custom_typeshed_dir,
122-
extra_search_path: extra_paths,
123-
venv_path,
124-
target_version,
125-
verbosity,
126-
watch,
127-
} = Args::parse_from(std::env::args().collect::<Vec<_>>());
128-
129-
if matches!(command, Some(Command::Server)) {
147+
let args = Args::parse_from(std::env::args().collect::<Vec<_>>());
148+
149+
if matches!(args.command, Some(Command::Server)) {
130150
return run_server().map(|()| ExitStatus::Success);
131151
}
132152

133-
let verbosity = verbosity.level();
153+
let verbosity = args.verbosity.level();
134154
countme::enable(verbosity.is_trace());
135155
let _guard = setup_tracing(verbosity)?;
136156

@@ -146,10 +166,12 @@ fn run() -> anyhow::Result<ExitStatus> {
146166
})?
147167
};
148168

149-
let cwd = current_directory
169+
let cwd = args
170+
.current_directory
171+
.as_ref()
150172
.map(|cwd| {
151173
if cwd.as_std_path().is_dir() {
152-
Ok(SystemPath::absolute(&cwd, &cli_base_path))
174+
Ok(SystemPath::absolute(cwd, &cli_base_path))
153175
} else {
154176
Err(anyhow!(
155177
"Provided current-directory path '{cwd}' is not a directory."
@@ -160,33 +182,18 @@ fn run() -> anyhow::Result<ExitStatus> {
160182
.unwrap_or_else(|| cli_base_path.clone());
161183

162184
let system = OsSystem::new(cwd.clone());
163-
let workspace_metadata = WorkspaceMetadata::from_path(system.current_directory(), &system)?;
164-
165-
// TODO: Verify the remaining search path settings eagerly.
166-
let site_packages = venv_path
167-
.map(|path| {
168-
VirtualEnvironment::new(path, &OsSystem::new(cli_base_path))
169-
.and_then(|venv| venv.site_packages_directories(&system))
170-
})
171-
.transpose()?
172-
.unwrap_or_default();
173-
174-
// TODO: Respect the settings from the workspace metadata. when resolving the program settings.
175-
let program_settings = ProgramSettings {
176-
target_version: target_version.into(),
177-
search_paths: SearchPathSettings {
178-
extra_paths,
179-
src_root: workspace_metadata.root().to_path_buf(),
180-
custom_typeshed: custom_typeshed_dir,
181-
site_packages,
182-
},
183-
};
185+
let cli_configuration = args.to_configuration(&cwd);
186+
let workspace_metadata = WorkspaceMetadata::from_path(
187+
system.current_directory(),
188+
&system,
189+
Some(cli_configuration.clone()),
190+
)?;
184191

185192
// TODO: Use the `program_settings` to compute the key for the database's persistent
186193
// cache and load the cache if it exists.
187-
let mut db = RootDatabase::new(workspace_metadata, program_settings, system)?;
194+
let mut db = RootDatabase::new(workspace_metadata, system)?;
188195

189-
let (main_loop, main_loop_cancellation_token) = MainLoop::new();
196+
let (main_loop, main_loop_cancellation_token) = MainLoop::new(cli_configuration);
190197

191198
// Listen to Ctrl+C and abort the watch mode.
192199
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
@@ -198,7 +205,7 @@ fn run() -> anyhow::Result<ExitStatus> {
198205
}
199206
})?;
200207

201-
let exit_status = if watch {
208+
let exit_status = if args.watch {
202209
main_loop.watch(&mut db)?
203210
} else {
204211
main_loop.run(&mut db)
@@ -238,17 +245,20 @@ struct MainLoop {
238245

239246
/// The file system watcher, if running in watch mode.
240247
watcher: Option<WorkspaceWatcher>,
248+
249+
cli_configuration: Configuration,
241250
}
242251

243252
impl MainLoop {
244-
fn new() -> (Self, MainLoopCancellationToken) {
253+
fn new(cli_configuration: Configuration) -> (Self, MainLoopCancellationToken) {
245254
let (sender, receiver) = crossbeam_channel::bounded(10);
246255

247256
(
248257
Self {
249258
sender: sender.clone(),
250259
receiver,
251260
watcher: None,
261+
cli_configuration,
252262
},
253263
MainLoopCancellationToken { sender },
254264
)
@@ -331,7 +341,7 @@ impl MainLoop {
331341
MainLoopMessage::ApplyChanges(changes) => {
332342
revision += 1;
333343
// Automatically cancels any pending queries and waits for them to complete.
334-
db.apply_changes(changes);
344+
db.apply_changes(changes, Some(&self.cli_configuration));
335345
if let Some(watcher) = self.watcher.as_mut() {
336346
watcher.update(db);
337347
}

0 commit comments

Comments
 (0)