Compare commits

...

3 Commits

Author SHA1 Message Date
Aria Desires
df090b40af fixup 2025-12-18 13:00:50 -05:00
Aria Desires
6d7aa3684c remove fixme 2025-12-18 12:52:35 -05:00
Aria Desires
4f22ab8b70 Make the default database truly statically infallible 2025-12-18 12:48:38 -05:00
27 changed files with 438 additions and 226 deletions

1
Cargo.lock generated
View File

@@ -3004,6 +3004,7 @@ dependencies = [
"tikv-jemallocator",
"tracing",
"ty_project",
"ty_python_semantic",
]
[[package]]

View File

@@ -54,6 +54,7 @@ ruff_python_formatter = { workspace = true, optional = true }
ruff_python_parser = { workspace = true, optional = true }
ruff_python_trivia = { workspace = true, optional = true }
ty_project = { workspace = true, optional = true }
ty_python_semantic = { workspace = true, optional = true }
divan = { workspace = true, optional = true }
anyhow = { workspace = true }
@@ -76,10 +77,11 @@ instrumented = [
"ruff_python_parser",
"ruff_python_trivia",
"ty_project",
"ty_python_semantic",
]
codspeed = ["codspeed-criterion-compat"]
# Enables benchmark that should only run with codspeed's walltime runner.
walltime = ["ruff_db/os", "ty_project", "divan"]
walltime = ["ruff_db/os", "ty_project", "ty_python_semantic", "divan"]
[target.'cfg(target_os = "windows")'.dev-dependencies]
mimalloc = { workspace = true }

View File

@@ -1,6 +1,7 @@
#![allow(clippy::disallowed_names)]
use ruff_benchmark::criterion;
use ruff_benchmark::real_world_projects::{InstalledProject, RealWorldProject};
use ty_python_semantic::FailStrategy;
use std::fmt::Write;
use std::ops::Range;
@@ -88,7 +89,7 @@ fn setup_tomllib_case() -> Case {
..Options::default()
});
let mut db = ProjectDatabase::new(metadata, system).unwrap();
let mut db = ProjectDatabase::new(metadata, system, &FailStrategy).unwrap();
let mut tomllib_files = FxHashSet::default();
let mut re: Option<File> = None;
@@ -235,7 +236,7 @@ fn setup_micro_case(code: &str) -> Case {
..Options::default()
});
let mut db = ProjectDatabase::new(metadata, system).unwrap();
let mut db = ProjectDatabase::new(metadata, system, &FailStrategy).unwrap();
let file = system_path_to_file(&db, SystemPathBuf::from(file_path)).unwrap();
db.set_check_mode(CheckMode::OpenFiles);
@@ -592,7 +593,7 @@ impl<'a> ProjectBenchmark<'a> {
..Options::default()
});
let mut db = ProjectDatabase::new(metadata, system).unwrap();
let mut db = ProjectDatabase::new(metadata, system, &FailStrategy).unwrap();
db.project().set_included_paths(
&mut db,

View File

@@ -1,5 +1,6 @@
use divan::{Bencher, bench};
use std::fmt::{Display, Formatter};
use ty_python_semantic::FailStrategy;
use rayon::ThreadPoolBuilder;
use ruff_benchmark::real_world_projects::{InstalledProject, RealWorldProject};
@@ -51,7 +52,7 @@ impl<'a> Benchmark<'a> {
..Options::default()
});
let mut db = ProjectDatabase::new(metadata, system).unwrap();
let mut db = ProjectDatabase::new(metadata, system, &FailStrategy).unwrap();
db.project().set_included_paths(
&mut db,

View File

@@ -9,8 +9,9 @@ use ruff_db::vendored::{VendoredFileSystem, VendoredFileSystemBuilder};
use ruff_python_ast::PythonVersion;
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{
Db, Program, ProgramSettings, PythonEnvironment, PythonPlatform, PythonVersionSource,
PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin, default_lint_registry,
Db, FailStrategy, Program, ProgramSettings, PythonEnvironment, PythonPlatform,
PythonVersionSource, PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin,
default_lint_registry,
};
static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| {
@@ -47,7 +48,7 @@ impl ModuleDb {
.into_vec();
}
let search_paths = search_paths
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.context("Invalid search path settings")?;
Program::from_settings(

View File

@@ -6,6 +6,7 @@ mod version;
pub use args::Cli;
use ty_project::metadata::settings::TerminalSettings;
use ty_python_semantic::FailStrategy;
use ty_static::EnvVars;
use std::fmt::Write;
@@ -129,7 +130,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
let project_options_overrides = ProjectOptionsOverrides::new(config_file, args.into_options());
project_metadata.apply_overrides(&project_options_overrides);
let mut db = ProjectDatabase::new(project_metadata, system)?;
let mut db = ProjectDatabase::new(project_metadata, system, &FailStrategy)?;
db.project()
.set_verbose(&mut db, verbosity >= VerbosityLevel::Verbose);

View File

@@ -15,7 +15,9 @@ use ty_project::metadata::pyproject::{PyProject, Tool};
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
use ty_project::watch::{ChangeEvent, ProjectWatcher, directory_watcher};
use ty_project::{Db, ProjectDatabase, ProjectMetadata};
use ty_python_semantic::{Module, ModuleName, PythonPlatform, resolve_module_confident};
use ty_python_semantic::{
FailStrategy, Module, ModuleName, PythonPlatform, resolve_module_confident,
};
struct TestCase {
db: ProjectDatabase,
@@ -434,7 +436,7 @@ where
}
}
let mut db = ProjectDatabase::new(project, system)?;
let mut db = ProjectDatabase::new(project, system, &FailStrategy)?;
if let Some(included_paths) = included_paths {
db.project().set_included_paths(&mut db, included_paths);

View File

@@ -19,7 +19,7 @@ use ty_project::metadata::Options;
use ty_project::metadata::options::EnvironmentOptions;
use ty_project::metadata::value::RelativePathBuf;
use ty_project::{ProjectDatabase, ProjectMetadata};
use ty_python_semantic::ModuleName;
use ty_python_semantic::{FailStrategy, ModuleName};
#[derive(Debug, clap::Parser)]
#[command(
@@ -290,7 +290,7 @@ impl Task {
..Options::default()
});
project_metadata.apply_configuration_files(&system)?;
let db = ProjectDatabase::new(project_metadata, system)?;
let db = ProjectDatabase::new(project_metadata, system, &FailStrategy)?;
Ok(Task {
db,
dir: project_path.to_path_buf(),

View File

@@ -15,7 +15,7 @@ use ruff_db::system::System;
use ruff_db::vendored::VendoredFileSystem;
use salsa::{Database, Event, Setter};
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{Db as SemanticDb, Program};
use ty_python_semantic::{Db as SemanticDb, MisconfigurationStrategy, Program};
mod changes;
@@ -44,7 +44,11 @@ pub struct ProjectDatabase {
}
impl ProjectDatabase {
pub fn new<S>(project_metadata: ProjectMetadata, system: S) -> anyhow::Result<Self>
pub fn new<S, Strategy: MisconfigurationStrategy>(
project_metadata: ProjectMetadata,
system: S,
strategy: &Strategy,
) -> Result<Self, Strategy::Error<anyhow::Error>>
where
S: System + 'static + Send + Sync + RefUnwindSafe,
{
@@ -72,13 +76,17 @@ impl ProjectDatabase {
// we may want to have a dedicated method for this?
// Initialize the `Program` singleton
let program_settings = project_metadata.to_program_settings(db.system(), db.vendored())?;
let program_settings = strategy.to_anyhow(project_metadata.to_program_settings(
db.system(),
db.vendored(),
strategy,
))?;
Program::from_settings(&db, program_settings);
db.project = Some(
Project::from_metadata(&db, project_metadata)
.map_err(|error| anyhow::anyhow!("{}", error.pretty(&db)))?,
);
db.project = Some(strategy.map_err(
Project::from_metadata(&db, project_metadata, strategy),
|error| anyhow::anyhow!("{}", error.pretty(&db)),
)?);
Ok(db)
}
@@ -525,7 +533,8 @@ pub(crate) mod tests {
use ruff_db::vendored::VendoredFileSystem;
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{
Program, ProgramSettings, PythonPlatform, PythonVersionWithSource, SearchPathSettings,
FailStrategy, Program, ProgramSettings, PythonPlatform, PythonVersionWithSource,
SearchPathSettings,
};
use crate::db::Db;
@@ -562,7 +571,7 @@ pub(crate) mod tests {
project: None,
};
let project = Project::from_metadata(&db, project).unwrap();
let project = Project::from_metadata(&db, project, &FailStrategy).unwrap();
db.project = Some(project);
db
}
@@ -571,7 +580,7 @@ pub(crate) mod tests {
let root = self.project().root(self);
let search_paths = SearchPathSettings::new(vec![root.to_path_buf()])
.to_search_paths(self.system(), self.vendored())
.to_search_paths(self.system(), self.vendored(), &FailStrategy)
.expect("Valid search path settings");
Program::from_settings(

View File

@@ -11,7 +11,7 @@ use ruff_db::files::{File, FileRootKind, Files};
use ruff_db::system::SystemPath;
use rustc_hash::FxHashSet;
use salsa::Setter;
use ty_python_semantic::Program;
use ty_python_semantic::{FailStrategy, Program};
/// Represents the result of applying changes to the project database.
pub struct ChangeResult {
@@ -260,7 +260,11 @@ impl ProjectDatabase {
metadata.apply_overrides(overrides);
}
match metadata.to_program_settings(self.system(), self.vendored()) {
match metadata.to_program_settings(
self.system(),
self.vendored(),
&FailStrategy,
) {
Ok(program_settings) => {
let program = Program::get(self);
program.update_from_settings(self, program_settings);
@@ -276,7 +280,7 @@ impl ProjectDatabase {
tracing::debug!("Reloading project after structural change");
project.reload(self, metadata);
} else {
match Project::from_metadata(self, metadata) {
match Project::from_metadata(self, metadata, &FailStrategy) {
Ok(new_project) => {
tracing::debug!("Replace project after structural change");
project = new_project;
@@ -304,10 +308,11 @@ impl ProjectDatabase {
return result;
} else if result.custom_stdlib_changed {
match project
.metadata(self)
.to_program_settings(self.system(), self.vendored())
{
match project.metadata(self).to_program_settings(
self.system(),
self.vendored(),
&FailStrategy,
) {
Ok(program_settings) => {
program.update_from_settings(self, program_settings);
}

View File

@@ -6,6 +6,8 @@ pub(crate) use portable::{
AbsolutePortableGlobPattern, PortableGlobError, PortableGlobKind, PortableGlobPattern,
};
use crate::metadata::options::DEFAULT_SRC_EXCLUDES;
mod exclude;
mod include;
mod portable;
@@ -60,6 +62,40 @@ impl IncludeExcludeFilter {
}
}
impl Default for IncludeExcludeFilter {
fn default() -> Self {
let mut includes = IncludeFilterBuilder::new();
includes
.add(
&PortableGlobPattern::parse("**", PortableGlobKind::Include)
.unwrap()
.into_absolute(""),
)
.expect("default include filter to be infallible");
let mut excludes = ExcludeFilterBuilder::new();
for pattern in DEFAULT_SRC_EXCLUDES {
PortableGlobPattern::parse(pattern, PortableGlobKind::Exclude)
.and_then(|exclude| Ok(excludes.add(&exclude.into_absolute(""))?))
.unwrap_or_else(|err| {
panic!(
"Expected default exclude to be valid glob but adding it failed with: {err}"
)
});
}
Self {
include: includes
.build()
.expect("default include filter to be infallible"),
exclude: excludes
.build()
.expect("default exclude filter to be infallible"),
}
}
}
impl std::fmt::Display for IncludeExcludeFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "include={}, exclude={}", &self.include, &self.exclude)

View File

@@ -27,9 +27,11 @@ use std::iter::FusedIterator;
use std::panic::{AssertUnwindSafe, UnwindSafe};
use std::sync::Arc;
use thiserror::Error;
use ty_python_semantic::add_inferred_python_version_hint_to_diagnostic;
use ty_python_semantic::lint::RuleSelection;
use ty_python_semantic::types::check_types;
use ty_python_semantic::{
FailStrategy, MisconfigurationStrategy, add_inferred_python_version_hint_to_diagnostic,
};
mod db;
mod files;
@@ -166,8 +168,15 @@ impl ProgressReporter for CollectReporter {
#[salsa::tracked]
impl Project {
pub fn from_metadata(db: &dyn Db, metadata: ProjectMetadata) -> Result<Self, ToSettingsError> {
let (settings, diagnostics) = metadata.options().to_settings(db, metadata.root())?;
pub fn from_metadata<Strategy: MisconfigurationStrategy>(
db: &dyn Db,
metadata: ProjectMetadata,
strategy: &Strategy,
) -> Result<Self, Strategy::Error<ToSettingsError>> {
let (settings, diagnostics) =
metadata
.options()
.to_settings(db, metadata.root(), strategy)?;
// This adds a file root for the project itself. This enables
// tracking of when changes are made to the files in a project
@@ -226,7 +235,10 @@ impl Project {
assert_eq!(self.root(db), metadata.root());
if &metadata != self.metadata(db) {
match metadata.options().to_settings(db, metadata.root()) {
match metadata
.options()
.to_settings(db, metadata.root(), &FailStrategy)
{
Ok((settings, settings_diagnostics)) => {
if self.settings(db) != &settings {
self.set_settings(db).to(Box::new(settings));

View File

@@ -5,7 +5,7 @@ use ruff_python_ast::name::Name;
use std::sync::Arc;
use thiserror::Error;
use ty_combine::Combine;
use ty_python_semantic::{MisconfigurationMode, ProgramSettings};
use ty_python_semantic::{FailStrategy, MisconfigurationStrategy, ProgramSettings};
use crate::metadata::options::ProjectOptionsOverrides;
use crate::metadata::pyproject::{Project, PyProject, PyProjectError, ResolveRequiresPythonError};
@@ -37,9 +37,6 @@ pub struct ProjectMetadata {
/// The path ordering doesn't imply precedence.
#[cfg_attr(test, serde(skip_serializing_if = "Vec::is_empty"))]
pub(super) extra_configuration_paths: Vec<SystemPathBuf>,
#[cfg_attr(test, serde(skip))]
pub(super) misconfiguration_mode: MisconfigurationMode,
}
impl ProjectMetadata {
@@ -50,7 +47,6 @@ impl ProjectMetadata {
root,
extra_configuration_paths: Vec::default(),
options: Options::default(),
misconfiguration_mode: MisconfigurationMode::Fail,
}
}
@@ -74,7 +70,6 @@ impl ProjectMetadata {
root: system.current_directory().to_path_buf(),
options,
extra_configuration_paths: vec![path],
misconfiguration_mode: MisconfigurationMode::Fail,
})
}
@@ -87,17 +82,17 @@ impl ProjectMetadata {
pyproject.tool.and_then(|tool| tool.ty).unwrap_or_default(),
root,
pyproject.project.as_ref(),
MisconfigurationMode::Fail,
&FailStrategy,
)
}
/// Loads a project from a set of options with an optional pyproject-project table.
pub fn from_options(
pub fn from_options<Strategy: MisconfigurationStrategy>(
mut options: Options,
root: SystemPathBuf,
project: Option<&Project>,
misconfiguration_mode: MisconfigurationMode,
) -> Result<Self, ResolveRequiresPythonError> {
strategy: &Strategy,
) -> Result<Self, Strategy::Error<ResolveRequiresPythonError>> {
let name = project
.and_then(|project| project.name.as_deref())
.map(|name| Name::new(&**name))
@@ -111,7 +106,13 @@ impl ProjectMetadata {
.as_ref()
.is_none_or(|env| env.python_version.is_none())
{
if let Some(requires_python) = project.resolve_requires_python_lower_bound()? {
let requires_python = strategy.fallback_opt(
project.resolve_requires_python_lower_bound(),
|err| {
tracing::debug!("skipping invalid requires_python lower bound: {err}");
},
)?;
if let Some(requires_python) = requires_python.flatten() {
let mut environment = options.environment.unwrap_or_default();
environment.python_version = Some(requires_python);
options.environment = Some(environment);
@@ -124,7 +125,6 @@ impl ProjectMetadata {
root,
options,
extra_configuration_paths: Vec::new(),
misconfiguration_mode,
})
}
@@ -202,7 +202,7 @@ impl ProjectMetadata {
pyproject
.as_ref()
.and_then(|pyproject| pyproject.project.as_ref()),
MisconfigurationMode::Fail,
&FailStrategy,
)
.map_err(|err| {
ProjectMetadataError::InvalidRequiresPythonConstraint {
@@ -277,18 +277,14 @@ impl ProjectMetadata {
&self.extra_configuration_paths
}
pub fn to_program_settings(
pub fn to_program_settings<Strategy: MisconfigurationStrategy>(
&self,
system: &dyn System,
vendored: &VendoredFileSystem,
) -> anyhow::Result<ProgramSettings> {
self.options.to_program_settings(
self.root(),
self.name(),
system,
vendored,
self.misconfiguration_mode,
)
strategy: &Strategy,
) -> Result<ProgramSettings, Strategy::Error<anyhow::Error>> {
self.options
.to_program_settings(self.root(), self.name(), system, vendored, strategy)
}
pub fn apply_overrides(&mut self, overrides: &ProjectOptionsOverrides) {

View File

@@ -30,7 +30,7 @@ use thiserror::Error;
use ty_combine::Combine;
use ty_python_semantic::lint::{Level, LintSource, RuleSelection};
use ty_python_semantic::{
MisconfigurationMode, ProgramSettings, PythonEnvironment, PythonPlatform,
MisconfigurationStrategy, ProgramSettings, PythonEnvironment, PythonPlatform,
PythonVersionFileSource, PythonVersionSource, PythonVersionWithSource, SearchPathSettings,
SearchPathValidationError, SearchPaths, SitePackagesPaths, SysPrefixPathOrigin,
};
@@ -111,14 +111,14 @@ impl Options {
Self::deserialize(deserializer)
}
pub(crate) fn to_program_settings(
pub(crate) fn to_program_settings<Strategy: MisconfigurationStrategy>(
&self,
project_root: &SystemPath,
project_name: &str,
system: &dyn System,
vendored: &VendoredFileSystem,
misconfiguration_mode: MisconfigurationMode,
) -> anyhow::Result<ProgramSettings> {
strategy: &Strategy,
) -> Result<ProgramSettings, Strategy::Error<anyhow::Error>> {
let environment = self.environment.or_default();
let options_python_version =
@@ -164,17 +164,11 @@ impl Options {
};
// If in safe-mode, fallback to None if this fails instead of erroring.
let python_environment = match python_environment {
Ok(python_environment) => python_environment,
Err(err) => {
if misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Default settings failed to discover local Python environment");
None
} else {
return Err(err);
}
}
};
let python_environment = strategy
.fallback_opt(python_environment, |_| {
tracing::debug!("Default settings failed to discover local Python environment");
})?
.flatten();
let self_site_packages = self_environment_search_paths(
python_environment
@@ -189,19 +183,10 @@ impl Options {
let site_packages_paths = python_environment
.site_packages_paths(system)
.context("Failed to discover the site-packages directory");
let site_packages_paths = match site_packages_paths {
Ok(paths) => paths,
Err(err) => {
if misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!(
"Default settings failed to discover site-packages directory"
);
SitePackagesPaths::default()
} else {
return Err(err);
}
}
};
let site_packages_paths = strategy.fallback(site_packages_paths, |_| {
tracing::debug!("Default settings failed to discover site-packages directory");
SitePackagesPaths::default()
})?;
self_site_packages.concatenate(site_packages_paths)
} else {
tracing::debug!("No virtual environment found");
@@ -226,15 +211,15 @@ impl Options {
.unwrap_or_default();
// Safe mode is handled inside this function, so we just assume this can't fail
let search_paths = self.to_search_paths(
let search_paths = strategy.to_anyhow(self.to_search_paths(
project_root,
project_name,
site_packages_paths,
real_stdlib_path,
system,
vendored,
misconfiguration_mode,
)?;
strategy,
))?;
tracing::info!(
"Python version: Python {python_version}, platform: {python_platform}",
@@ -249,7 +234,7 @@ impl Options {
}
#[expect(clippy::too_many_arguments)]
fn to_search_paths(
fn to_search_paths<Strategy: MisconfigurationStrategy>(
&self,
project_root: &SystemPath,
project_name: &str,
@@ -257,8 +242,8 @@ impl Options {
real_stdlib_path: Option<SystemPathBuf>,
system: &dyn System,
vendored: &VendoredFileSystem,
misconfiguration_mode: MisconfigurationMode,
) -> Result<SearchPaths, SearchPathValidationError> {
strategy: &Strategy,
) -> Result<SearchPaths, Strategy::Error<SearchPathValidationError>> {
let environment = self.environment.or_default();
let src = self.src.or_default();
@@ -372,17 +357,17 @@ impl Options {
.map(|path| path.absolute(project_root, system)),
site_packages_paths: site_packages_paths.into_vec(),
real_stdlib_path,
misconfiguration_mode,
};
settings.to_search_paths(system, vendored)
settings.to_search_paths(system, vendored, strategy)
}
pub(crate) fn to_settings(
pub(crate) fn to_settings<Strategy: MisconfigurationStrategy>(
&self,
db: &dyn Db,
project_root: &SystemPath,
) -> Result<(Settings, Vec<OptionDiagnostic>), ToSettingsError> {
strategy: &Strategy,
) -> Result<(Settings, Vec<OptionDiagnostic>), Strategy::Error<ToSettingsError>> {
let mut diagnostics = Vec::new();
let rules = self.to_rule_selection(db, &mut diagnostics);
@@ -432,7 +417,8 @@ impl Options {
diagnostic: err,
output_format: terminal.output_format,
color: colored::control::SHOULD_COLORIZE.should_colorize(),
})?;
});
let src = strategy.fallback(src, |_| SrcSettings::default())?;
let overrides = self
.to_overrides_settings(db, project_root, &mut diagnostics)
@@ -440,7 +426,8 @@ impl Options {
diagnostic: err,
output_format: terminal.output_format,
color: colored::control::SHOULD_COLORIZE.should_colorize(),
})?;
});
let overrides = strategy.fallback(overrides, |_| Vec::new())?;
let settings = Settings {
rules: Arc::new(rules),
@@ -918,7 +905,7 @@ impl Rules {
}
/// Default exclude patterns for src options.
const DEFAULT_SRC_EXCLUDES: &[&str] = &[
pub(crate) const DEFAULT_SRC_EXCLUDES: &[&str] = &[
"**/.bzr/",
"**/.direnv/",
"**/.eggs/",

View File

@@ -67,6 +67,14 @@ pub struct SrcSettings {
pub respect_ignore_files: bool,
pub files: IncludeExcludeFilter,
}
impl SrcSettings {
pub(crate) fn default() -> Self {
Self {
respect_ignore_files: true,
files: IncludeExcludeFilter::default(),
}
}
}
/// A single configuration override that applies to files matching specific patterns.
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]

View File

@@ -23,8 +23,8 @@ pub(crate) mod tests {
use crate::program::{Program, SearchPathSettings};
use crate::{
ProgramSettings, PythonPlatform, PythonVersionSource, PythonVersionWithSource,
default_lint_registry,
FailStrategy, ProgramSettings, PythonPlatform, PythonVersionSource,
PythonVersionWithSource, default_lint_registry,
};
use super::Db;
@@ -188,7 +188,7 @@ pub(crate) mod tests {
},
python_platform: self.python_platform,
search_paths: SearchPathSettings::new(vec![src_root])
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.context("Invalid search path settings")?,
},
);

View File

@@ -17,8 +17,8 @@ pub use module_resolver::{
resolve_real_module_confident, resolve_real_shadowable_module, system_module_search_paths,
};
pub use program::{
MisconfigurationMode, Program, ProgramSettings, PythonVersionFileSource, PythonVersionSource,
PythonVersionWithSource, SearchPathSettings,
FailStrategy, MisconfigurationStrategy, Program, ProgramSettings, PythonVersionFileSource,
PythonVersionSource, PythonVersionWithSource, SearchPathSettings, UseDefaultStrategy,
};
pub use python_platform::PythonPlatform;
use rustc_hash::FxHasher;

View File

@@ -395,7 +395,7 @@ mod tests {
};
use crate::module_resolver::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};
use crate::program::{Program, ProgramSettings, SearchPathSettings};
use crate::{PythonPlatform, PythonVersionSource, PythonVersionWithSource};
use crate::{FailStrategy, PythonPlatform, PythonVersionSource, PythonVersionWithSource};
use super::list_modules;
@@ -940,6 +940,8 @@ mod tests {
fn symlink() -> anyhow::Result<()> {
use anyhow::Context;
use crate::FailStrategy;
let mut db = TestDb::new();
let temp_dir = tempfile::TempDir::with_prefix("PREFIX-SENTINEL")?;
@@ -980,7 +982,7 @@ mod tests {
site_packages_paths: vec![site_packages],
..SearchPathSettings::new(vec![src])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search path settings"),
},
);
@@ -1488,7 +1490,7 @@ not_a_directory
site_packages_paths: vec![venv_site_packages],
..SearchPathSettings::new(vec![src.to_path_buf()])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search path settings"),
},
);
@@ -1542,7 +1544,7 @@ not_a_directory
site_packages_paths: vec![venv_site_packages, system_site_packages],
..SearchPathSettings::new(vec![SystemPathBuf::from("/src")])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search path settings"),
},
);
@@ -1627,7 +1629,7 @@ not_a_directory
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(vec![src])
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("valid search path settings"),
},
);
@@ -1671,7 +1673,7 @@ not_a_directory
site_packages_paths: vec![site_packages],
..SearchPathSettings::new(vec![project_directory])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.unwrap(),
},
);
@@ -1826,7 +1828,7 @@ not_a_directory
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(vec![project_directory])
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.unwrap(),
},
);

View File

@@ -50,7 +50,7 @@ use ruff_python_ast::{
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::module_resolver::typeshed::{TypeshedVersions, vendored_typeshed_versions};
use crate::program::MisconfigurationMode;
use crate::program::MisconfigurationStrategy;
use crate::{Program, SearchPathSettings};
use super::module::{Module, ModuleKind};
@@ -554,11 +554,12 @@ impl SearchPaths {
/// This method also implements the typing spec's [module resolution order].
///
/// [module resolution order]: https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering
pub(crate) fn from_settings(
pub(crate) fn from_settings<Strategy: MisconfigurationStrategy>(
settings: &SearchPathSettings,
system: &dyn System,
vendored: &VendoredFileSystem,
) -> Result<Self, SearchPathValidationError> {
strategy: &Strategy,
) -> Result<Self, Strategy::Error<SearchPathValidationError>> {
fn canonicalize(path: &SystemPath, system: &dyn System) -> SystemPathBuf {
system
.canonicalize_path(path)
@@ -571,7 +572,6 @@ impl SearchPaths {
custom_typeshed: typeshed,
site_packages_paths,
real_stdlib_path,
misconfiguration_mode,
} = settings;
let mut static_paths = vec![];
@@ -580,30 +580,21 @@ impl SearchPaths {
let path = canonicalize(path, system);
tracing::debug!("Adding extra search-path `{path}`");
match SearchPath::extra(system, path) {
Ok(path) => static_paths.push(path),
Err(err) => {
if *misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Skipping invalid extra search-path: {err}");
} else {
return Err(err);
}
}
}
let path = strategy.fallback_opt(SearchPath::extra(system, path), |err| {
tracing::debug!("Skipping invalid extra search-path: {err}");
})?;
static_paths.extend(path);
}
for src_root in src_roots {
tracing::debug!("Adding first-party search path `{src_root}`");
match SearchPath::first_party(system, src_root.to_path_buf()) {
Ok(path) => static_paths.push(path),
Err(err) => {
if *misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Skipping invalid first-party search-path: {err}");
} else {
return Err(err);
}
}
}
let path = strategy.fallback_opt(
SearchPath::first_party(system, src_root.to_path_buf()),
|err| {
tracing::debug!("Skipping invalid first-party search-path: {err}");
},
)?;
static_paths.extend(path);
}
let (typeshed_versions, stdlib_path) = if let Some(typeshed) = typeshed {
@@ -623,20 +614,13 @@ impl SearchPaths {
.and_then(|versions_content| Ok(versions_content.parse()?))
.and_then(|parsed| Ok((parsed, SearchPath::custom_stdlib(system, &typeshed)?)));
match results {
Ok(results) => results,
Err(err) => {
if settings.misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Skipping custom-stdlib search-path: {err}");
(
vendored_typeshed_versions(vendored),
SearchPath::vendored_stdlib(),
)
} else {
return Err(err);
}
}
}
strategy.fallback(results, |err| {
tracing::debug!("Skipping custom-stdlib search-path: {err}");
(
vendored_typeshed_versions(vendored),
SearchPath::vendored_stdlib(),
)
})?
} else {
tracing::debug!("Using vendored stdlib");
(
@@ -646,17 +630,9 @@ impl SearchPaths {
};
let real_stdlib_path = if let Some(path) = real_stdlib_path {
match SearchPath::real_stdlib(system, path.clone()) {
Ok(path) => Some(path),
Err(err) => {
if *misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Skipping invalid real-stdlib search-path: {err}");
None
} else {
return Err(err);
}
}
}
strategy.fallback_opt(SearchPath::real_stdlib(system, path.clone()), |err| {
tracing::debug!("Skipping invalid real-stdlib search-path: {err}");
})?
} else {
None
};
@@ -665,16 +641,11 @@ impl SearchPaths {
for path in site_packages_paths {
tracing::debug!("Adding site-packages search path `{path}`");
match SearchPath::site_packages(system, path.clone()) {
Ok(path) => site_packages.push(path),
Err(err) => {
if settings.misconfiguration_mode == MisconfigurationMode::UseDefault {
tracing::debug!("Skipping invalid real-stdlib search-path: {err}");
} else {
return Err(err);
}
}
}
let path =
strategy.fallback_opt(SearchPath::site_packages(system, path.clone()), |err| {
tracing::debug!("Skipping invalid real-stdlib search-path: {err}");
})?;
site_packages.extend(path);
}
// TODO vendor typeshed's third-party stubs as well as the stdlib and
@@ -1743,7 +1714,7 @@ mod tests {
use crate::module_name::ModuleName;
use crate::module_resolver::module::ModuleKind;
use crate::module_resolver::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};
use crate::{ProgramSettings, PythonPlatform, PythonVersionWithSource};
use crate::{FailStrategy, ProgramSettings, PythonPlatform, PythonVersionWithSource};
use super::*;
@@ -2269,7 +2240,8 @@ mod tests {
use anyhow::Context;
use crate::{
PythonPlatform, PythonVersionSource, PythonVersionWithSource, program::Program,
FailStrategy, PythonPlatform, PythonVersionSource, PythonVersionWithSource,
program::Program,
};
use ruff_db::system::{OsSystem, SystemPath};
@@ -2313,7 +2285,7 @@ mod tests {
site_packages_paths: vec![site_packages],
..SearchPathSettings::new(vec![src.clone()])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search path settings"),
},
);
@@ -2857,7 +2829,7 @@ not_a_directory
site_packages_paths: vec![venv_site_packages, system_site_packages],
..SearchPathSettings::new(vec![SystemPathBuf::from("/src")])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search path settings"),
},
);
@@ -2930,7 +2902,7 @@ not_a_directory
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(vec![src])
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("valid search path settings"),
},
);
@@ -2973,7 +2945,7 @@ not_a_directory
site_packages_paths: vec![site_packages.clone()],
..SearchPathSettings::new(vec![project_directory])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.unwrap(),
},
);

View File

@@ -8,7 +8,9 @@ use ruff_python_ast::PythonVersion;
use crate::db::tests::TestDb;
use crate::program::{Program, SearchPathSettings};
use crate::{ProgramSettings, PythonPlatform, PythonVersionSource, PythonVersionWithSource};
use crate::{
FailStrategy, ProgramSettings, PythonPlatform, PythonVersionSource, PythonVersionWithSource,
};
/// A test case for the module resolver.
///
@@ -280,7 +282,7 @@ impl TestCaseBuilder<MockedTypeshed> {
site_packages_paths: vec![site_packages.clone()],
..SearchPathSettings::new(vec![src.clone()])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("valid search path settings"),
},
);
@@ -351,7 +353,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
site_packages_paths: vec![site_packages.clone()],
..SearchPathSettings::new(vec![src.clone()])
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("valid search path settings"),
},
);

View File

@@ -163,15 +163,195 @@ impl Default for PythonVersionWithSource {
}
}
#[derive(PartialEq, Eq, Debug, Copy, Clone, get_size2::GetSize)]
pub enum MisconfigurationMode {
/// Settings Failure Is Not An Error.
/// A type that never exists.
///
/// In Rust if you have Result<T, Never> the compiler knows `Err` is impossible
/// and you can just write `let Ok(val) = result;`
pub enum Never {}
/// Generic handling of two possible approaches to an Error:
///
/// * [`FailStrategy`]: The code should simply fail
/// * [`UseDefaultStrategy`]: The chode should apply default values and never fail
///
/// Any function that wants to be made generic over these approaches should be changed thusly.
///
/// Old:
///
/// ```ignore
/// fn do_thing()
/// -> Result<T, E>
/// {
/// let x = something_fallible()?;
/// Ok(x)
/// }
/// ```
///
/// New:
///
/// ```ignore
/// fn do_thing<Strategy: MisconfigurationStrategy>(strategy: &Strategy)
/// -> Result<T, Strategy::Error<E>>
/// {
/// let x = strategy.fallback(something_fallible(), |err| {
/// tracing::debug!("Failed to get value: {err}");
/// MyType::default()
/// })?;
/// Ok(x)
/// }
/// ```
///
/// The key trick is instead of returning `Result<T, E>` your function should
/// return `Result<T, Strategy::Error<E>>`. Which simplifies to:
///
/// * [`FailStrategy`]: `Result<T, E>`
/// * [`UseDefaultStrategy`]: `Result<T, Never>` ~= `T`
///
/// Notably, if your function returns `Result<T, Strategy::Error<E>>` you will
/// be *statically prevented* from returning an `Err` without going through
/// [`MisconfigurationStrategy::fallback`][] or [`MisconfigurationStrategy::fallback_opt`][]
/// which ensure you're handling both approaches (or you wrote an `unwrap` but
/// those standout far more than adding a new `?` to a function that must be able to Not Fail).
///
/// Also, for any caller that passes in [`UseDefaultStrategy`], they will be able
/// to write `let Ok(val) = do_thing(&UseDefaultStrategy);` instead of having to
/// write an `unwrap()`.
pub trait MisconfigurationStrategy {
/// * [`FailStrategy`][]: `E`
/// * [`UseDefaultStrategy`][]: `Never`
type Error<E>;
/// Try to get the value out of a Result that we need to proceed.
///
/// This is used by the default database, which we are incentivized to make infallible,
/// while still trying to "do our best" to set things up properly where we can.
UseDefault,
/// Settings Failure Is An Error.
Fail,
/// If [`UseDefaultStrategy`], on `Err` this will call `fallback_fn` to compute
/// a default value and always return `Ok`.
///
/// If [`FailStrategy`] this is a no-op and will return the Result.
fn fallback<T, E>(
&self,
result: Result<T, E>,
fallback_fn: impl FnOnce(E) -> T,
) -> Result<T, Self::Error<E>>;
/// Try to get the value out of a Result that we can do without.
///
/// If [`UseDefaultStrategy`], this will call `fallback_fn` to report an issue
/// (i.e. you can invoke `tracing::debug!` or something) and then return `None`.
///
/// If [`FailStrategy`] this is a no-op and will return the Result (but `Ok` => `Ok(Some)`).
fn fallback_opt<T, E>(
&self,
result: Result<T, E>,
fallback_fn: impl FnOnce(E),
) -> Result<Option<T>, Self::Error<E>>;
/// Convenience to convert the inner `Error` to `anyhow::Error`
fn to_anyhow<T, E>(
&self,
result: Result<T, Self::Error<E>>,
) -> Result<T, Self::Error<anyhow::Error>>
where
anyhow::Error: From<E>;
/// Convenience to map the inner `Error`
fn map_err<T, E1, E2>(
&self,
result: Result<T, Self::Error<E1>>,
map_err: impl FnOnce(E1) -> E2,
) -> Result<T, Self::Error<E2>>;
}
/// A [`MisconfigurationStrategy`] that refuses to *ever* return an `Err`
/// and instead substitutes default values or skips functionality.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct UseDefaultStrategy;
impl MisconfigurationStrategy for UseDefaultStrategy {
type Error<E> = Never;
fn fallback<T, E>(
&self,
result: Result<T, E>,
fallback_fn: impl FnOnce(E) -> T,
) -> Result<T, Self::Error<E>> {
Ok(result.unwrap_or_else(fallback_fn))
}
fn fallback_opt<T, E>(
&self,
result: Result<T, E>,
fallback_fn: impl FnOnce(E),
) -> Result<Option<T>, Self::Error<E>> {
match result {
Ok(val) => Ok(Some(val)),
Err(e) => {
fallback_fn(e);
Ok(None)
}
}
}
fn to_anyhow<T, E>(
&self,
result: Result<T, Self::Error<E>>,
) -> Result<T, Self::Error<anyhow::Error>>
where
anyhow::Error: From<E>,
{
let Ok(val) = result;
Ok(val)
}
fn map_err<T, E1, E2>(
&self,
result: Result<T, Self::Error<E1>>,
_map_err: impl FnOnce(E1) -> E2,
) -> Result<T, Self::Error<E2>> {
let Ok(val) = result;
Ok(val)
}
}
/// A [`MisconfigurationStrategy`] that happily fails whenever
/// an important `Err` is encountered.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct FailStrategy;
impl MisconfigurationStrategy for FailStrategy {
type Error<E> = E;
fn fallback<T, E>(
&self,
result: Result<T, E>,
_fallback_fn: impl FnOnce(E) -> T,
) -> Result<T, Self::Error<E>> {
result
}
fn fallback_opt<T, E>(
&self,
result: Result<T, E>,
_fallback_fn: impl FnOnce(E),
) -> Result<Option<T>, Self::Error<E>> {
result.map(Some)
}
fn to_anyhow<T, E>(
&self,
result: Result<T, Self::Error<E>>,
) -> Result<T, Self::Error<anyhow::Error>>
where
anyhow::Error: From<E>,
{
Ok(result?)
}
fn map_err<T, E1, E2>(
&self,
result: Result<T, Self::Error<E1>>,
map_err: impl FnOnce(E1) -> E2,
) -> Result<T, Self::Error<E2>> {
result.map_err(map_err)
}
}
/// Configures the search paths for module resolution.
@@ -198,9 +378,6 @@ pub struct SearchPathSettings {
/// We should ideally only ever use this for things like goto-definition,
/// where typeshed isn't the right answer.
pub real_stdlib_path: Option<SystemPathBuf>,
/// How to handle apparent misconfiguration
pub misconfiguration_mode: MisconfigurationMode,
}
impl SearchPathSettings {
@@ -218,15 +395,15 @@ impl SearchPathSettings {
custom_typeshed: None,
site_packages_paths: vec![],
real_stdlib_path: None,
misconfiguration_mode: MisconfigurationMode::Fail,
}
}
pub fn to_search_paths(
pub fn to_search_paths<Strategy: MisconfigurationStrategy>(
&self,
system: &dyn System,
vendored: &VendoredFileSystem,
) -> Result<SearchPaths, SearchPathValidationError> {
SearchPaths::from_settings(self, system, vendored)
strategy: &Strategy,
) -> Result<SearchPaths, Strategy::Error<SearchPathValidationError>> {
SearchPaths::from_settings(self, system, vendored, strategy)
}
}

View File

@@ -8,8 +8,8 @@ use ruff_python_ast::PythonVersion;
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::pull_types::pull_types;
use ty_python_semantic::{
Program, ProgramSettings, PythonPlatform, PythonVersionSource, PythonVersionWithSource,
SearchPathSettings, default_lint_registry,
FailStrategy, Program, ProgramSettings, PythonPlatform, PythonVersionSource,
PythonVersionWithSource, SearchPathSettings, default_lint_registry,
};
use test_case::test_case;
@@ -200,7 +200,7 @@ impl CorpusDb {
},
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(vec![])
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.unwrap(),
},
);

View File

@@ -28,7 +28,7 @@ use ty_project::{ChangeResult, CheckMode, Db as _, ProjectDatabase, ProjectMetad
use index::DocumentError;
use options::GlobalOptions;
use ty_python_semantic::MisconfigurationMode;
use ty_python_semantic::{FailStrategy, UseDefaultStrategy};
pub(crate) use self::options::InitializationOptions;
pub use self::options::{ClientOptions, DiagnosticMode};
@@ -497,7 +497,7 @@ impl Session {
metadata.apply_overrides(overrides);
}
ProjectDatabase::new(metadata, system.clone())
ProjectDatabase::new(metadata, system.clone(), &FailStrategy)
});
let (root, db) = match project {
@@ -513,15 +513,14 @@ impl Session {
Please refer to the logs for more details.",
));
let db_with_default_settings = ProjectMetadata::from_options(
let Ok(metadata) = ProjectMetadata::from_options(
Options::default(),
root,
None,
MisconfigurationMode::UseDefault,
)
.context("Failed to convert default options to metadata")
.and_then(|metadata| ProjectDatabase::new(metadata, system))
.expect("Default configuration to be valid");
&UseDefaultStrategy,
);
let Ok(db_with_default_settings) =
ProjectDatabase::new(metadata, system, &UseDefaultStrategy);
let default_root = db_with_default_settings
.project()
.root(&db_with_default_settings)
@@ -1231,16 +1230,16 @@ impl DefaultProject {
let index = index.unwrap();
let system = LSPSystem::new(index.clone(), fallback_system.clone());
let metadata = ProjectMetadata::from_options(
let Ok(metadata) = ProjectMetadata::from_options(
Options::default(),
system.current_directory().to_path_buf(),
None,
MisconfigurationMode::UseDefault,
)
.unwrap();
&UseDefaultStrategy,
);
let Ok(db) = ProjectDatabase::new(metadata, system, &UseDefaultStrategy);
ProjectState {
db: ProjectDatabase::new(metadata, system).unwrap(),
db,
untracked_files_with_pushed_diagnostics: Vec::new(),
}
})

View File

@@ -503,7 +503,8 @@ mod tests {
use ruff_python_trivia::textwrap::dedent;
use ruff_source_file::OneIndexed;
use ty_python_semantic::{
Program, ProgramSettings, PythonPlatform, PythonVersionWithSource, SearchPathSettings,
FailStrategy, Program, ProgramSettings, PythonPlatform, PythonVersionWithSource,
SearchPathSettings,
};
fn get_assertions(source: &str) -> InlineFileAssertions {
@@ -513,7 +514,7 @@ mod tests {
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(Vec::new())
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.unwrap(),
};
Program::init_or_update(&mut db, settings);

View File

@@ -19,7 +19,7 @@ use std::fmt::{Display, Write};
use ty_python_semantic::pull_types::pull_types;
use ty_python_semantic::types::{UNDEFINED_REVEAL, check_types};
use ty_python_semantic::{
MisconfigurationMode, Module, Program, ProgramSettings, PythonEnvironment, PythonPlatform,
FailStrategy, Module, Program, ProgramSettings, PythonEnvironment, PythonPlatform,
PythonVersionSource, PythonVersionWithSource, SearchPath, SearchPathSettings,
SysPrefixPathOrigin, list_modules, resolve_module_confident,
};
@@ -441,9 +441,8 @@ fn run_test(
custom_typeshed: custom_typeshed_path.map(SystemPath::to_path_buf),
site_packages_paths,
real_stdlib_path: None,
misconfiguration_mode: MisconfigurationMode::Fail,
}
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Failed to resolve search path settings"),
};

View File

@@ -404,7 +404,8 @@ mod tests {
use ruff_source_file::OneIndexed;
use ruff_text_size::TextRange;
use ty_python_semantic::{
Program, ProgramSettings, PythonPlatform, PythonVersionWithSource, SearchPathSettings,
FailStrategy, Program, ProgramSettings, PythonPlatform, PythonVersionWithSource,
SearchPathSettings,
};
struct ExpectedDiagnostic {
@@ -453,7 +454,7 @@ mod tests {
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(Vec::new())
.to_search_paths(db.system(), db.vendored())
.to_search_paths(db.system(), db.vendored(), &FailStrategy)
.expect("Valid search paths settings"),
};
Program::init_or_update(&mut db, settings);

View File

@@ -26,7 +26,7 @@ use ty_project::metadata::value::ValueSource;
use ty_project::watch::{ChangeEvent, ChangedKind, CreatedKind, DeletedKind};
use ty_project::{CheckMode, ProjectMetadata};
use ty_project::{Db, ProjectDatabase};
use ty_python_semantic::{MisconfigurationMode, Program};
use ty_python_semantic::{FailStrategy, Program};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
@@ -99,15 +99,12 @@ impl Workspace {
let system = WasmSystem::new(SystemPath::new(root));
let project = ProjectMetadata::from_options(
options,
SystemPathBuf::from(root),
None,
MisconfigurationMode::Fail,
)
.map_err(into_error)?;
let project =
ProjectMetadata::from_options(options, SystemPathBuf::from(root), None, &FailStrategy)
.map_err(into_error)?;
let mut db = ProjectDatabase::new(project, system.clone()).map_err(into_error)?;
let mut db =
ProjectDatabase::new(project, system.clone(), &FailStrategy).map_err(into_error)?;
// By default, it will check all files in the project but we only want to check the open
// files in the playground.
@@ -132,12 +129,12 @@ impl Workspace {
options,
self.db.project().root(&self.db).to_path_buf(),
None,
MisconfigurationMode::Fail,
&FailStrategy,
)
.map_err(into_error)?;
let program_settings = project
.to_program_settings(&self.system, self.db.vendored())
.to_program_settings(&self.system, self.db.vendored(), &FailStrategy)
.map_err(into_error)?;
Program::get(&self.db).update_from_settings(&mut self.db, program_settings);