mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 18:02:58 +00:00
Move resolver tests out to top-level (#5424)
## Summary These are really tests for the entire crate.
This commit is contained in:
parent
1d2d015bc5
commit
4d90a5a9bc
8 changed files with 896 additions and 898 deletions
|
@ -12,3 +12,888 @@ mod python_platform;
|
|||
mod python_version;
|
||||
mod resolver;
|
||||
mod search;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use log::debug;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::execution_environment::ExecutionEnvironment;
|
||||
use crate::host;
|
||||
use crate::import_result::{ImportResult, ImportType};
|
||||
use crate::module_descriptor::ImportModuleDescriptor;
|
||||
use crate::python_platform::PythonPlatform;
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::resolver::resolve_import;
|
||||
|
||||
/// Create a file at the given path with the given content.
|
||||
fn create(path: PathBuf, content: &str) -> io::Result<PathBuf> {
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
let mut f = File::create(&path)?;
|
||||
f.write_all(content.as_bytes())?;
|
||||
f.sync_all()?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Create an empty file at the given path.
|
||||
fn empty(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "")
|
||||
}
|
||||
|
||||
/// Create a partial `py.typed` file at the given path.
|
||||
fn partial(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "partial\n")
|
||||
}
|
||||
|
||||
/// Create a `py.typed` file at the given path.
|
||||
fn typed(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "# typed")
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ResolverOptions {
|
||||
extra_paths: Vec<PathBuf>,
|
||||
library: Option<PathBuf>,
|
||||
stub_path: Option<PathBuf>,
|
||||
typeshed_path: Option<PathBuf>,
|
||||
venv_path: Option<PathBuf>,
|
||||
venv: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn resolve_options(
|
||||
source_file: impl AsRef<Path>,
|
||||
name: &str,
|
||||
root: impl Into<PathBuf>,
|
||||
options: ResolverOptions,
|
||||
) -> ImportResult {
|
||||
let ResolverOptions {
|
||||
extra_paths,
|
||||
library,
|
||||
stub_path,
|
||||
typeshed_path,
|
||||
venv_path,
|
||||
venv,
|
||||
} = options;
|
||||
|
||||
let execution_environment = ExecutionEnvironment {
|
||||
root: root.into(),
|
||||
python_version: PythonVersion::Py37,
|
||||
python_platform: PythonPlatform::Darwin,
|
||||
extra_paths,
|
||||
};
|
||||
|
||||
let module_descriptor = ImportModuleDescriptor {
|
||||
leading_dots: name.chars().take_while(|c| *c == '.').count(),
|
||||
name_parts: name
|
||||
.chars()
|
||||
.skip_while(|c| *c == '.')
|
||||
.collect::<String>()
|
||||
.split('.')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect(),
|
||||
imported_symbols: Vec::new(),
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
typeshed_path,
|
||||
stub_path,
|
||||
venv_path,
|
||||
venv,
|
||||
};
|
||||
|
||||
let host = host::StaticHost::new(if let Some(library) = library {
|
||||
vec![library]
|
||||
} else {
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
resolve_import(
|
||||
source_file.as_ref(),
|
||||
&execution_environment,
|
||||
&module_descriptor,
|
||||
&config,
|
||||
&host,
|
||||
)
|
||||
}
|
||||
|
||||
fn setup() {
|
||||
env_logger::builder().is_test(true).try_init().ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_file_exists() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_pyi = empty(library.join("myLib-stubs").join("partialStub.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
// TODO(charlie): Pyright matches on `libraryRoot, 'myLib', 'partialStub.pyi'` here.
|
||||
// But that file doesn't exist. There's some kind of transform.
|
||||
vec![PathBuf::new(), partial_stub_pyi]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_init_exists() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let partial_stub_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
// TODO(charlie): Pyright matches on `libraryRoot, 'myLib', '__init__.pyi'` here.
|
||||
// But that file doesn't exist. There's some kind of transform.
|
||||
vec![partial_stub_init_pyi]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn side_by_side_files() -> io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
empty(library.join("myLib/partialStub.pyi"))?;
|
||||
empty(library.join("myLib/partialStub.py"))?;
|
||||
empty(library.join("myLib/partialStub2.py"))?;
|
||||
let my_file = empty(root.join("myFile.py"))?;
|
||||
let side_by_side_stub_file = empty(library.join("myLib-stubs/partialStub.pyi"))?;
|
||||
let partial_stub_file = empty(library.join("myLib-stubs/partialStub2.pyi"))?;
|
||||
|
||||
// Stub package wins over original package (per PEP 561 rules).
|
||||
let side_by_side_result = resolve_options(
|
||||
&my_file,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
assert!(side_by_side_result.is_import_found);
|
||||
assert!(side_by_side_result.is_stub_file);
|
||||
assert_eq!(
|
||||
side_by_side_result.resolved_paths,
|
||||
vec![PathBuf::new(), side_by_side_stub_file]
|
||||
);
|
||||
|
||||
// Side by side stub doesn't completely disable partial stub.
|
||||
let partial_stub_result = resolve_options(
|
||||
&my_file,
|
||||
"myLib.partialStub2",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
assert!(partial_stub_result.is_import_found);
|
||||
assert!(partial_stub_result.is_stub_file);
|
||||
assert_eq!(
|
||||
partial_stub_result.resolved_paths,
|
||||
vec![PathBuf::new(), partial_stub_file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib-stubs/stub.pyi"))?;
|
||||
empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If fully typed stub package exists, that wins over the real package.
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_namespace_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib-stubs/stub.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py.clone(),
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If fully typed stub package exists, that wins over the real package.
|
||||
assert!(result.is_import_found);
|
||||
assert!(!result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![PathBuf::new(), partial_stub_py]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_in_typing_folder_over_partial_stub_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typing_folder = root.join("typing");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_pyi = empty(typing_folder.join("myLib.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
stub_path: Some(typing_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If the package exists in typing folder, that gets picked up first (so we resolve to
|
||||
// `myLib.pyi`).
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_package_in_typing_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typing_folder = root.join("typing");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(typing_folder.join("myLib-stubs/py.typed"))?;
|
||||
let my_lib_stubs_init_pyi = empty(typing_folder.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
stub_path: Some(typing_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If the package exists in typing folder, that gets picked up first (so we resolve to
|
||||
// `myLib.pyi`).
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_stubs_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeshed_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(typeshed_folder.join("stubs/myLibPackage/myLib.pyi"))?;
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let my_lib_stubs_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// Stub packages win over typeshed.
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_stubs_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn py_typed_file() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib/__init__.py"))?;
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let package_py_typed = typed(library.join("myLib/py.typed"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
package_py_typed,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// Partial stub package always overrides original package.
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![partial_stub_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn py_typed_library() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
typed(library.join("os/py.typed"))?;
|
||||
let init_py = empty(library.join("os/__init__.py"))?;
|
||||
let typeshed_init_pyi = empty(typeshed_folder.join("stubs/os/os/__init__.pyi"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
typeshed_init_pyi,
|
||||
"os",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.resolved_paths, vec![init_py]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_py_typed_library() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("os/__init__.py"))?;
|
||||
let typeshed_init_pyi = empty(typeshed_folder.join("stubs/os/os/__init__.pyi"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
typeshed_init_pyi.clone(),
|
||||
"os",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(result.resolved_paths, vec![typeshed_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file1 = empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let test_init = empty(root.join("test/__init__.py"))?;
|
||||
let test_file1 = empty(root.join("test/file1.py"))?;
|
||||
let test_file2 = empty(root.join("test/file2.py"))?;
|
||||
|
||||
let result = resolve_options(test_file2, "test.file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![test_init, test_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_under_src_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let nested_init = empty(root.join("src/nested/__init__.py"))?;
|
||||
let nested_file1 = empty(root.join("src/nested/file1.py"))?;
|
||||
let nested_file2 = empty(root.join("src/nested/file2.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
nested_file2,
|
||||
"nested.file1",
|
||||
root,
|
||||
ResolverOptions::default(),
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![nested_init, nested_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_file_sub_under_containing_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let nested_file1 = empty(root.join("src/nested/file1.py"))?;
|
||||
let nested_file2 = empty(root.join("src/nested/nested2/file2.py"))?;
|
||||
|
||||
let result = resolve_options(nested_file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![nested_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_under_lib_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib/file1.py"))?;
|
||||
let file2 = empty(library.join("myLib/file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
debug!("result: {:?}", result);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_1() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file = empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package1_init = empty(root.join("package1/a/__init__.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
vec![package1_init, PathBuf::new(), PathBuf::new(), file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_2() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file = empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package1_init = empty(root.join("package1/a/b/c/__init__.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/b/c/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
vec![PathBuf::new(), PathBuf::new(), package1_init, file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_3() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_4() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("package1/a/b/__init__.py"))?;
|
||||
empty(root.join("package1/a/b/c.py"))?;
|
||||
empty(root.join("package2/a/__init__.py"))?;
|
||||
let package2_a_b_init = empty(root.join("package2/a/b/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_a_b_init,
|
||||
"a.b.c",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// New tests, don't exist upstream.
|
||||
#[test]
|
||||
fn relative_import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file1 = empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, ".file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_relative_import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "..file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_standard_library() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"os",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_first_party() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.jobs.scheduler_job_runner",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_stub_file() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.compat.functools",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_namespace_package() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.providers.google.cloud.hooks.gcs",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_third_party() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"sqlalchemy.orm",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -723,7 +723,7 @@ fn resolve_import_strict<Host: host::Host>(
|
|||
/// 3. If a stub file was found, find the "best" match for the import, disallowing stub files.
|
||||
/// 4. If the import wasn't resolved, try to resolve it in the parent directory, then the parent's
|
||||
/// parent, and so on, until the import root is reached.
|
||||
fn resolve_import<Host: host::Host>(
|
||||
pub(crate) fn resolve_import<Host: host::Host>(
|
||||
source_file: &Path,
|
||||
execution_environment: &ExecutionEnvironment,
|
||||
module_descriptor: &ImportModuleDescriptor,
|
||||
|
@ -780,888 +780,3 @@ fn resolve_import<Host: host::Host>(
|
|||
|
||||
ImportResult::not_found()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use log::debug;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::execution_environment::ExecutionEnvironment;
|
||||
use crate::host;
|
||||
use crate::import_result::{ImportResult, ImportType};
|
||||
use crate::module_descriptor::ImportModuleDescriptor;
|
||||
use crate::python_platform::PythonPlatform;
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::resolver::resolve_import;
|
||||
|
||||
/// Create a file at the given path with the given content.
|
||||
fn create(path: PathBuf, content: &str) -> io::Result<PathBuf> {
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
let mut f = File::create(&path)?;
|
||||
f.write_all(content.as_bytes())?;
|
||||
f.sync_all()?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Create an empty file at the given path.
|
||||
fn empty(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "")
|
||||
}
|
||||
|
||||
/// Create a partial `py.typed` file at the given path.
|
||||
fn partial(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "partial\n")
|
||||
}
|
||||
|
||||
/// Create a `py.typed` file at the given path.
|
||||
fn typed(path: PathBuf) -> io::Result<PathBuf> {
|
||||
create(path, "# typed")
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ResolverOptions {
|
||||
extra_paths: Vec<PathBuf>,
|
||||
library: Option<PathBuf>,
|
||||
stub_path: Option<PathBuf>,
|
||||
typeshed_path: Option<PathBuf>,
|
||||
venv_path: Option<PathBuf>,
|
||||
venv: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn resolve_options(
|
||||
source_file: impl AsRef<Path>,
|
||||
name: &str,
|
||||
root: impl Into<PathBuf>,
|
||||
options: ResolverOptions,
|
||||
) -> ImportResult {
|
||||
let ResolverOptions {
|
||||
extra_paths,
|
||||
library,
|
||||
stub_path,
|
||||
typeshed_path,
|
||||
venv_path,
|
||||
venv,
|
||||
} = options;
|
||||
|
||||
let execution_environment = ExecutionEnvironment {
|
||||
root: root.into(),
|
||||
python_version: PythonVersion::Py37,
|
||||
python_platform: PythonPlatform::Darwin,
|
||||
extra_paths,
|
||||
};
|
||||
|
||||
let module_descriptor = ImportModuleDescriptor {
|
||||
leading_dots: name.chars().take_while(|c| *c == '.').count(),
|
||||
name_parts: name
|
||||
.chars()
|
||||
.skip_while(|c| *c == '.')
|
||||
.collect::<String>()
|
||||
.split('.')
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect(),
|
||||
imported_symbols: Vec::new(),
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
typeshed_path,
|
||||
stub_path,
|
||||
venv_path,
|
||||
venv,
|
||||
};
|
||||
|
||||
let host = host::StaticHost::new(if let Some(library) = library {
|
||||
vec![library]
|
||||
} else {
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
resolve_import(
|
||||
source_file.as_ref(),
|
||||
&execution_environment,
|
||||
&module_descriptor,
|
||||
&config,
|
||||
&host,
|
||||
)
|
||||
}
|
||||
|
||||
fn setup() {
|
||||
env_logger::builder().is_test(true).try_init().ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_file_exists() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_pyi = empty(library.join("myLib-stubs").join("partialStub.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
// TODO(charlie): Pyright matches on `libraryRoot, 'myLib', 'partialStub.pyi'` here.
|
||||
// But that file doesn't exist. There's some kind of transform.
|
||||
vec![PathBuf::new(), partial_stub_pyi]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_init_exists() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let partial_stub_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
// TODO(charlie): Pyright matches on `libraryRoot, 'myLib', '__init__.pyi'` here.
|
||||
// But that file doesn't exist. There's some kind of transform.
|
||||
vec![partial_stub_init_pyi]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn side_by_side_files() -> io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
empty(library.join("myLib/partialStub.pyi"))?;
|
||||
empty(library.join("myLib/partialStub.py"))?;
|
||||
empty(library.join("myLib/partialStub2.py"))?;
|
||||
let my_file = empty(root.join("myFile.py"))?;
|
||||
let side_by_side_stub_file = empty(library.join("myLib-stubs/partialStub.pyi"))?;
|
||||
let partial_stub_file = empty(library.join("myLib-stubs/partialStub2.pyi"))?;
|
||||
|
||||
// Stub package wins over original package (per PEP 561 rules).
|
||||
let side_by_side_result = resolve_options(
|
||||
&my_file,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
assert!(side_by_side_result.is_import_found);
|
||||
assert!(side_by_side_result.is_stub_file);
|
||||
assert_eq!(
|
||||
side_by_side_result.resolved_paths,
|
||||
vec![PathBuf::new(), side_by_side_stub_file]
|
||||
);
|
||||
|
||||
// Side by side stub doesn't completely disable partial stub.
|
||||
let partial_stub_result = resolve_options(
|
||||
&my_file,
|
||||
"myLib.partialStub2",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
assert!(partial_stub_result.is_import_found);
|
||||
assert!(partial_stub_result.is_stub_file);
|
||||
assert_eq!(
|
||||
partial_stub_result.resolved_paths,
|
||||
vec![PathBuf::new(), partial_stub_file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib-stubs/stub.pyi"))?;
|
||||
empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py,
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If fully typed stub package exists, that wins over the real package.
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_namespace_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib-stubs/stub.pyi"))?;
|
||||
let partial_stub_py = empty(library.join("myLib/partialStub.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
partial_stub_py.clone(),
|
||||
"myLib.partialStub",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If fully typed stub package exists, that wins over the real package.
|
||||
assert!(result.is_import_found);
|
||||
assert!(!result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![PathBuf::new(), partial_stub_py]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stub_in_typing_folder_over_partial_stub_package() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typing_folder = root.join("typing");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_pyi = empty(typing_folder.join("myLib.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
stub_path: Some(typing_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If the package exists in typing folder, that gets picked up first (so we resolve to
|
||||
// `myLib.pyi`).
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_stub_package_in_typing_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typing_folder = root.join("typing");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
partial(typing_folder.join("myLib-stubs/py.typed"))?;
|
||||
let my_lib_stubs_init_pyi = empty(typing_folder.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
stub_path: Some(typing_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// If the package exists in typing folder, that gets picked up first (so we resolve to
|
||||
// `myLib.pyi`).
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_stubs_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeshed_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(typeshed_folder.join("stubs/myLibPackage/myLib.pyi"))?;
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let my_lib_stubs_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let my_lib_init_py = empty(library.join("myLib/__init__.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
my_lib_init_py,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// Stub packages win over typeshed.
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![my_lib_stubs_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn py_typed_file() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib/__init__.py"))?;
|
||||
partial(library.join("myLib-stubs/py.typed"))?;
|
||||
let partial_stub_init_pyi = empty(library.join("myLib-stubs/__init__.pyi"))?;
|
||||
let package_py_typed = typed(library.join("myLib/py.typed"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
package_py_typed,
|
||||
"myLib",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// Partial stub package always overrides original package.
|
||||
assert!(result.is_import_found);
|
||||
assert!(result.is_stub_file);
|
||||
assert_eq!(result.resolved_paths, vec![partial_stub_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn py_typed_library() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
typed(library.join("os/py.typed"))?;
|
||||
let init_py = empty(library.join("os/__init__.py"))?;
|
||||
let typeshed_init_pyi = empty(typeshed_folder.join("stubs/os/os/__init__.pyi"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
typeshed_init_pyi,
|
||||
"os",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.resolved_paths, vec![init_py]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_py_typed_library() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
let typeshed_folder = root.join("ts");
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("os/__init__.py"))?;
|
||||
let typeshed_init_pyi = empty(typeshed_folder.join("stubs/os/os/__init__.pyi"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
typeshed_init_pyi.clone(),
|
||||
"os",
|
||||
root,
|
||||
ResolverOptions {
|
||||
library: Some(library),
|
||||
typeshed_path: Some(typeshed_folder),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::ThirdParty);
|
||||
assert_eq!(result.resolved_paths, vec![typeshed_init_pyi]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file1 = empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let test_init = empty(root.join("test/__init__.py"))?;
|
||||
let test_file1 = empty(root.join("test/file1.py"))?;
|
||||
let test_file2 = empty(root.join("test/file2.py"))?;
|
||||
|
||||
let result = resolve_options(test_file2, "test.file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![test_init, test_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_under_src_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let nested_init = empty(root.join("src/nested/__init__.py"))?;
|
||||
let nested_file1 = empty(root.join("src/nested/file1.py"))?;
|
||||
let nested_file2 = empty(root.join("src/nested/file2.py"))?;
|
||||
|
||||
let result = resolve_options(
|
||||
nested_file2,
|
||||
"nested.file1",
|
||||
root,
|
||||
ResolverOptions::default(),
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![nested_init, nested_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_file_sub_under_containing_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let nested_file1 = empty(root.join("src/nested/file1.py"))?;
|
||||
let nested_file2 = empty(root.join("src/nested/nested2/file2.py"))?;
|
||||
|
||||
let result = resolve_options(nested_file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![nested_file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_side_by_side_file_sub_under_lib_folder() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let library = temp_dir.path().join("lib").join("site-packages");
|
||||
|
||||
empty(library.join("myLib/file1.py"))?;
|
||||
let file2 = empty(library.join("myLib/file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "file1", root, ResolverOptions::default());
|
||||
|
||||
debug!("result: {:?}", result);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_1() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file = empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package1_init = empty(root.join("package1/a/__init__.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
vec![package1_init, PathBuf::new(), PathBuf::new(), file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_2() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file = empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package1_init = empty(root.join("package1/a/b/c/__init__.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/b/c/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(
|
||||
result.resolved_paths,
|
||||
vec![PathBuf::new(), PathBuf::new(), package1_init, file]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_3() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("package1/a/b/c/d.py"))?;
|
||||
let package2_init = empty(root.join("package2/a/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_init,
|
||||
"a.b.c.d",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_namespace_package_4() -> io::Result<()> {
|
||||
// See: https://github.com/microsoft/pyright/issues/5089.
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("package1/a/b/__init__.py"))?;
|
||||
empty(root.join("package1/a/b/c.py"))?;
|
||||
empty(root.join("package2/a/__init__.py"))?;
|
||||
let package2_a_b_init = empty(root.join("package2/a/b/__init__.py"))?;
|
||||
|
||||
let package1 = root.join("package1");
|
||||
let package2 = root.join("package2");
|
||||
|
||||
let result = resolve_options(
|
||||
package2_a_b_init,
|
||||
"a.b.c",
|
||||
root,
|
||||
ResolverOptions {
|
||||
extra_paths: vec![package1, package2],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// New tests, don't exist upstream.
|
||||
#[test]
|
||||
fn relative_import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
let file1 = empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, ".file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(result.is_import_found);
|
||||
assert_eq!(result.import_type, ImportType::Local);
|
||||
assert_eq!(result.resolved_paths, vec![file1]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_relative_import_side_by_side_file_root() -> io::Result<()> {
|
||||
setup();
|
||||
|
||||
let temp_dir = TempDir::new()?;
|
||||
let root = temp_dir.path();
|
||||
|
||||
empty(root.join("file1.py"))?;
|
||||
let file2 = empty(root.join("file2.py"))?;
|
||||
|
||||
let result = resolve_options(file2, "..file1", root, ResolverOptions::default());
|
||||
|
||||
assert!(!result.is_import_found);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_standard_library() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"os",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_first_party() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.jobs.scheduler_job_runner",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_stub_file() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.compat.functools",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_namespace_package() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"airflow.providers.google.cloud.hooks.gcs",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn airflow_third_party() {
|
||||
setup();
|
||||
|
||||
let root = PathBuf::from("./resources/test/airflow");
|
||||
let source_file = root.join("airflow/api/common/mark_tasks.py");
|
||||
|
||||
let result = resolve_options(
|
||||
source_file,
|
||||
"sqlalchemy.orm",
|
||||
root.clone(),
|
||||
ResolverOptions {
|
||||
venv_path: Some(root),
|
||||
venv: Some(PathBuf::from("venv")),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_python_resolver/src/resolver.rs
|
||||
source: crates/ruff_python_resolver/src/lib.rs
|
||||
expression: result
|
||||
---
|
||||
ImportResult {
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_python_resolver/src/resolver.rs
|
||||
source: crates/ruff_python_resolver/src/lib.rs
|
||||
expression: result
|
||||
---
|
||||
ImportResult {
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_python_resolver/src/resolver.rs
|
||||
source: crates/ruff_python_resolver/src/lib.rs
|
||||
expression: result
|
||||
---
|
||||
ImportResult {
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_python_resolver/src/resolver.rs
|
||||
source: crates/ruff_python_resolver/src/lib.rs
|
||||
expression: result
|
||||
---
|
||||
ImportResult {
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_python_resolver/src/resolver.rs
|
||||
source: crates/ruff_python_resolver/src/lib.rs
|
||||
expression: result
|
||||
---
|
||||
ImportResult {
|
Loading…
Add table
Add a link
Reference in a new issue