From 4d90a5a9bc8e5fe0d10d5ab6c8163889b484a4c3 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 28 Jun 2023 13:25:37 -0400 Subject: [PATCH] Move resolver tests out to top-level (#5424) ## Summary These are really tests for the entire crate. --- ...yter__notebook__tests__import_sorting.snap | 12 +- crates/ruff_python_resolver/src/lib.rs | 885 +++++++++++++++++ crates/ruff_python_resolver/src/resolver.rs | 887 +----------------- ...resolver__tests__airflow_first_party.snap} | 2 +- ...er__tests__airflow_namespace_package.snap} | 2 +- ...ver__tests__airflow_standard_library.snap} | 2 +- ...n_resolver__tests__airflow_stub_file.snap} | 2 +- ...resolver__tests__airflow_third_party.snap} | 2 +- 8 files changed, 896 insertions(+), 898 deletions(-) rename crates/ruff_python_resolver/src/snapshots/{ruff_python_resolver__resolver__tests__airflow_first_party.snap => ruff_python_resolver__tests__airflow_first_party.snap} (94%) rename crates/ruff_python_resolver/src/snapshots/{ruff_python_resolver__resolver__tests__airflow_namespace_package.snap => ruff_python_resolver__tests__airflow_namespace_package.snap} (95%) rename crates/ruff_python_resolver/src/snapshots/{ruff_python_resolver__resolver__tests__airflow_standard_library.snap => ruff_python_resolver__tests__airflow_standard_library.snap} (92%) rename crates/ruff_python_resolver/src/snapshots/{ruff_python_resolver__resolver__tests__airflow_stub_file.snap => ruff_python_resolver__tests__airflow_stub_file.snap} (97%) rename crates/ruff_python_resolver/src/snapshots/{ruff_python_resolver__resolver__tests__airflow_third_party.snap => ruff_python_resolver__tests__airflow_third_party.snap} (97%) diff --git a/crates/ruff/src/jupyter/snapshots/ruff__jupyter__notebook__tests__import_sorting.snap b/crates/ruff/src/jupyter/snapshots/ruff__jupyter__notebook__tests__import_sorting.snap index 8470981be3..240556c375 100644 --- a/crates/ruff/src/jupyter/snapshots/ruff__jupyter__notebook__tests__import_sorting.snap +++ b/crates/ruff/src/jupyter/snapshots/ruff__jupyter__notebook__tests__import_sorting.snap @@ -25,14 +25,12 @@ isort.ipynb:cell 1:1:1: I001 [*] Import block is un-sorted or un-formatted isort.ipynb:cell 2:1:1: I001 [*] Import block is un-sorted or un-formatted | -2 | import random -3 | import math -4 | / from typing import Any -5 | | import collections -6 | | # Newline should be added here +1 | / from typing import Any +2 | | import collections +3 | | # Newline should be added here | |_^ I001 -7 | def foo(): -8 | pass +4 | def foo(): +5 | pass | = help: Organize imports diff --git a/crates/ruff_python_resolver/src/lib.rs b/crates/ruff_python_resolver/src/lib.rs index 9cc1458b1f..505c623099 100644 --- a/crates/ruff_python_resolver/src/lib.rs +++ b/crates/ruff_python_resolver/src/lib.rs @@ -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 { + 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 { + create(path, "") + } + + /// Create a partial `py.typed` file at the given path. + fn partial(path: PathBuf) -> io::Result { + create(path, "partial\n") + } + + /// Create a `py.typed` file at the given path. + fn typed(path: PathBuf) -> io::Result { + create(path, "# typed") + } + + #[derive(Debug, Default)] + struct ResolverOptions { + extra_paths: Vec, + library: Option, + stub_path: Option, + typeshed_path: Option, + venv_path: Option, + venv: Option, + } + + fn resolve_options( + source_file: impl AsRef, + name: &str, + root: impl Into, + 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::() + .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); + } +} diff --git a/crates/ruff_python_resolver/src/resolver.rs b/crates/ruff_python_resolver/src/resolver.rs index 3cb3eca260..e175edff44 100644 --- a/crates/ruff_python_resolver/src/resolver.rs +++ b/crates/ruff_python_resolver/src/resolver.rs @@ -723,7 +723,7 @@ fn resolve_import_strict( /// 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( +pub(crate) fn resolve_import( source_file: &Path, execution_environment: &ExecutionEnvironment, module_descriptor: &ImportModuleDescriptor, @@ -780,888 +780,3 @@ fn resolve_import( 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 { - 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 { - create(path, "") - } - - /// Create a partial `py.typed` file at the given path. - fn partial(path: PathBuf) -> io::Result { - create(path, "partial\n") - } - - /// Create a `py.typed` file at the given path. - fn typed(path: PathBuf) -> io::Result { - create(path, "# typed") - } - - #[derive(Debug, Default)] - struct ResolverOptions { - extra_paths: Vec, - library: Option, - stub_path: Option, - typeshed_path: Option, - venv_path: Option, - venv: Option, - } - - fn resolve_options( - source_file: impl AsRef, - name: &str, - root: impl Into, - 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::() - .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); - } -} diff --git a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_first_party.snap b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_first_party.snap similarity index 94% rename from crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_first_party.snap rename to crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_first_party.snap index d2197f6cab..864f3e1a12 100644 --- a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_first_party.snap +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_first_party.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff_python_resolver/src/resolver.rs +source: crates/ruff_python_resolver/src/lib.rs expression: result --- ImportResult { diff --git a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_namespace_package.snap b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_namespace_package.snap similarity index 95% rename from crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_namespace_package.snap rename to crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_namespace_package.snap index 15954d4c98..d8f9d08207 100644 --- a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_namespace_package.snap +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_namespace_package.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff_python_resolver/src/resolver.rs +source: crates/ruff_python_resolver/src/lib.rs expression: result --- ImportResult { diff --git a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_standard_library.snap b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_standard_library.snap similarity index 92% rename from crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_standard_library.snap rename to crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_standard_library.snap index 3262ce7f6f..8429372b42 100644 --- a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_standard_library.snap +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_standard_library.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff_python_resolver/src/resolver.rs +source: crates/ruff_python_resolver/src/lib.rs expression: result --- ImportResult { diff --git a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_stub_file.snap b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_stub_file.snap similarity index 97% rename from crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_stub_file.snap rename to crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_stub_file.snap index 37f26c80d7..faf1604f1b 100644 --- a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_stub_file.snap +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_stub_file.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff_python_resolver/src/resolver.rs +source: crates/ruff_python_resolver/src/lib.rs expression: result --- ImportResult { diff --git a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_third_party.snap b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_third_party.snap similarity index 97% rename from crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_third_party.snap rename to crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_third_party.snap index ed13cadb03..488711b8f1 100644 --- a/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_third_party.snap +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__tests__airflow_third_party.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff_python_resolver/src/resolver.rs +source: crates/ruff_python_resolver/src/lib.rs expression: result --- ImportResult {