[red-knot] Fix python setting in mdtests, and rewrite a site-packages test as an mdtest (#17222)
## Summary This PR does the following things: - Fixes the `python` configuration setting for mdtest (added in https://github.com/astral-sh/ruff/pull/17221) so that it expects a path pointing to a venv's `sys.prefix` variable rather than the a path pointing to the venv's `site-packages` subdirectory. This brings the `python` setting in mdtest in sync with our CLI `--python` flag. - Tweaks mdtest so that it automatically creates a valid `pyvenv.cfg` file for you if you don't specify one. This makes it much more ergonomic to write an mdtest with a custom `python` setting: red-knot will reject a `python` setting that points to a directory that doesn't have a `pyvenv.cfg` file in it - Tweaks mdtest so that it doesn't check a custom `pyvenv.cfg` as Python source code if you _do_ add a custom `pyvenv.cfg` file for your mock virtual environment in an mdtest. (You get a lot of diagnostics about Python syntax errors in the `pyvenv.cfg` file, otherwise!) - Rewrites the test added in https://github.com/astral-sh/ruff/pull/17178 as an mdtest, and deletes the original test that was added in that PR ## Test Plan I verified that the new mdtest fails if I revert the changes to `resolver.rs` that were added in https://github.com/astral-sh/ruff/pull/17178
This commit is contained in:
@@ -236,3 +236,36 @@ X: int = 42
|
||||
```py
|
||||
from .parser import X # error: [unresolved-import]
|
||||
```
|
||||
|
||||
## Relative imports in `site-packages`
|
||||
|
||||
Relative imports in `site-packages` are correctly resolved even when the `site-packages` search path
|
||||
is a subdirectory of the first-party search path. Note that mdtest sets the first-party search path
|
||||
to `/src/`, which is why the virtual environment in this test is a subdirectory of `/src/`, even
|
||||
though this is not how a typical Python project would be structured:
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python = "/src/.venv"
|
||||
python-version = "3.13"
|
||||
```
|
||||
|
||||
`/src/bar.py`:
|
||||
|
||||
```py
|
||||
from foo import A
|
||||
|
||||
reveal_type(A) # revealed: Literal[A]
|
||||
```
|
||||
|
||||
`/src/.venv/<path-to-site-packages>/foo/__init__.py`:
|
||||
|
||||
```py
|
||||
from .a import A as A
|
||||
```
|
||||
|
||||
`/src/.venv/<path-to-site-packages>/foo/a.py`:
|
||||
|
||||
```py
|
||||
class A: ...
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@ pub(crate) mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::{default_lint_registry, ProgramSettings, PythonPath, PythonPlatform};
|
||||
use crate::{default_lint_registry, ProgramSettings, PythonPlatform};
|
||||
|
||||
use super::Db;
|
||||
use crate::lint::{LintRegistry, RuleSelection};
|
||||
@@ -139,8 +139,6 @@ pub(crate) mod tests {
|
||||
python_version: PythonVersion,
|
||||
/// Target Python platform
|
||||
python_platform: PythonPlatform,
|
||||
/// Paths to the directory to use for `site-packages`
|
||||
site_packages: Vec<SystemPathBuf>,
|
||||
/// Path and content pairs for files that should be present
|
||||
files: Vec<(&'a str, &'a str)>,
|
||||
}
|
||||
@@ -150,7 +148,6 @@ pub(crate) mod tests {
|
||||
Self {
|
||||
python_version: PythonVersion::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
site_packages: vec![],
|
||||
files: vec![],
|
||||
}
|
||||
}
|
||||
@@ -169,14 +166,6 @@ pub(crate) mod tests {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_site_packages_search_path(
|
||||
mut self,
|
||||
path: &(impl AsRef<SystemPath> + ?Sized),
|
||||
) -> Self {
|
||||
self.site_packages.push(path.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn build(self) -> anyhow::Result<TestDb> {
|
||||
let mut db = TestDb::new();
|
||||
|
||||
@@ -186,15 +175,12 @@ pub(crate) mod tests {
|
||||
db.write_files(self.files)
|
||||
.context("Failed to write test files")?;
|
||||
|
||||
let mut search_paths = SearchPathSettings::new(vec![src_root]);
|
||||
search_paths.python_path = PythonPath::KnownSitePackages(self.site_packages);
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: self.python_version,
|
||||
python_platform: self.python_platform,
|
||||
search_paths,
|
||||
search_paths: SearchPathSettings::new(vec![src_root]),
|
||||
},
|
||||
)
|
||||
.context("Failed to configure Program settings")?;
|
||||
|
||||
@@ -10,6 +10,7 @@ pub use module_resolver::{resolve_module, system_module_search_paths, KnownModul
|
||||
pub use program::{Program, ProgramSettings, PythonPath, SearchPathSettings};
|
||||
pub use python_platform::PythonPlatform;
|
||||
pub use semantic_model::{HasType, SemanticModel};
|
||||
pub use site_packages::SysPrefixPathOrigin;
|
||||
|
||||
pub mod ast_node_ref;
|
||||
mod db;
|
||||
|
||||
@@ -7392,7 +7392,7 @@ impl StringPartsCollector {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::db::tests::{setup_db, TestDb, TestDbBuilder};
|
||||
use crate::db::tests::{setup_db, TestDb};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::symbol::FileScopeId;
|
||||
use crate::semantic_index::{global_scope, semantic_index, symbol_table, use_def_map};
|
||||
@@ -7400,7 +7400,7 @@ mod tests {
|
||||
use crate::types::check_types;
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::system::{DbWithWritableSystem as _, SystemPath};
|
||||
use ruff_db::system::DbWithWritableSystem as _;
|
||||
use ruff_db::testing::{assert_function_query_was_not_run, assert_function_query_was_run};
|
||||
|
||||
use super::*;
|
||||
@@ -7556,26 +7556,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relative_import_resolution_in_site_packages_when_site_packages_is_subdirectory_of_first_party_search_path(
|
||||
) {
|
||||
let project_root = SystemPath::new("/src");
|
||||
let foo_dot_py = project_root.join("foo.py");
|
||||
let site_packages = project_root.join(".venv/lib/python3.13/site-packages");
|
||||
|
||||
let db = TestDbBuilder::new()
|
||||
.with_site_packages_search_path(&site_packages)
|
||||
.with_file(&foo_dot_py, "from bar import A")
|
||||
.with_file(&site_packages.join("bar/__init__.py"), "from .a import *")
|
||||
.with_file(&site_packages.join("bar/a.py"), "class A: ...")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_file_diagnostics(&db, foo_dot_py.as_str(), &[]);
|
||||
let a_symbol = get_symbol(&db, foo_dot_py.as_str(), &[], "A");
|
||||
assert!(a_symbol.expect_type().is_class_literal());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep695_type_params() {
|
||||
let mut db = setup_db();
|
||||
|
||||
Reference in New Issue
Block a user