diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a33c8353c7..5209b8b84b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,9 @@ exclude: | crates/ruff/src/rules/.*/snapshots/.*| crates/ruff_cli/resources/.*| crates/ruff_python_formatter/resources/.*| - crates/ruff_python_formatter/tests/snapshots/.* + crates/ruff_python_formatter/tests/snapshots/.*| + crates/ruff_python_resolver/resources/.*| + crates/ruff_python_resolver/tests/snapshots/.* )$ repos: diff --git a/Cargo.lock b/Cargo.lock index 57c612d87e..f463b1601f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2095,6 +2095,7 @@ name = "ruff_python_resolver" version = "0.0.0" dependencies = [ "env_logger", + "insta", "log", "tempfile", ] diff --git a/crates/ruff_python_resolver/Cargo.toml b/crates/ruff_python_resolver/Cargo.toml index 2d454e88cb..2ec3942f6e 100644 --- a/crates/ruff_python_resolver/Cargo.toml +++ b/crates/ruff_python_resolver/Cargo.toml @@ -19,3 +19,4 @@ log = { workspace = true } [dev-dependencies] env_logger = "0.10.0" tempfile = "3.6.0" +insta = { workspace = true } diff --git a/crates/ruff_python_resolver/resources/test/airflow/README.md b/crates/ruff_python_resolver/resources/test/airflow/README.md new file mode 100644 index 0000000000..803b7c401b --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/README.md @@ -0,0 +1,3 @@ +# airflow + +This is a mock subset of the Airflow repository, used to test module resolution. diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/__init__.py @@ -0,0 +1 @@ + diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/api/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/__init__.py @@ -0,0 +1 @@ + diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/__init__.py @@ -0,0 +1 @@ + diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/mark_tasks.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/mark_tasks.py new file mode 100644 index 0000000000..9b13b3153e --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/api/common/mark_tasks.py @@ -0,0 +1,14 @@ +# Standard library. +import os + +# First-party. +from airflow.jobs.scheduler_job_runner import SchedulerJobRunner + +# Stub file. +from airflow.compat.functools import cached_property + +# Namespace package. +from airflow.providers.google.cloud.hooks.gcs import GCSHook + +# Third-party. +from sqlalchemy.orm import Query diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/__init__.py new file mode 100644 index 0000000000..13a83393a9 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.pyi b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.pyi new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/compat/functools.pyi @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/jobs/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/jobs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/jobs/scheduler_job_runner.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/jobs/scheduler_job_runner.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/jobs/scheduler_job_runner.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/hooks/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/hooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/hooks/gcs.py b/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/hooks/gcs.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/airflow/providers/google/cloud/hooks/gcs.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/__init__.py @@ -0,0 +1 @@ + diff --git a/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/__init__.py b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/base.py b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/base.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/base.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/dependency.py b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/dependency.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/dependency.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/query.py b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/query.py new file mode 100644 index 0000000000..75a6dead35 --- /dev/null +++ b/crates/ruff_python_resolver/resources/test/airflow/venv/lib/python3.11/site-packages/sqlalchemy/orm/query.py @@ -0,0 +1 @@ +"""Empty file included to support filesystem-based resolver tests.""" diff --git a/crates/ruff_python_resolver/src/implicit_imports.rs b/crates/ruff_python_resolver/src/implicit_imports.rs index 2c42f5450a..25a7d040c6 100644 --- a/crates/ruff_python_resolver/src/implicit_imports.rs +++ b/crates/ruff_python_resolver/src/implicit_imports.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; @@ -24,8 +24,8 @@ pub(crate) struct ImplicitImport { } /// Find the "implicit" imports within the namespace package at the given path. -pub(crate) fn find(dir_path: &Path, exclusions: &[&Path]) -> HashMap { - let mut implicit_imports = HashMap::new(); +pub(crate) fn find(dir_path: &Path, exclusions: &[&Path]) -> BTreeMap { + let mut implicit_imports = BTreeMap::new(); // Enumerate all files and directories in the path, expanding links. let Ok(entries) = fs::read_dir(dir_path) else { @@ -128,14 +128,14 @@ pub(crate) fn find(dir_path: &Path, exclusions: &[&Path]) -> HashMap, + implicit_imports: &BTreeMap, imported_symbols: &[String], -) -> Option> { +) -> Option> { if implicit_imports.is_empty() || imported_symbols.is_empty() { return None; } - let mut filtered_imports = HashMap::new(); + let mut filtered_imports = BTreeMap::new(); for implicit_import in implicit_imports.values() { if imported_symbols.contains(&implicit_import.name) { filtered_imports.insert(implicit_import.name.clone(), implicit_import.clone()); diff --git a/crates/ruff_python_resolver/src/import_result.rs b/crates/ruff_python_resolver/src/import_result.rs index 6ca7bd245c..72161cb3e1 100644 --- a/crates/ruff_python_resolver/src/import_result.rs +++ b/crates/ruff_python_resolver/src/import_result.rs @@ -1,9 +1,9 @@ //! Interface that describes the output of the import resolver. -use crate::implicit_imports::ImplicitImport; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::path::PathBuf; +use crate::implicit_imports::ImplicitImport; use crate::py_typed::PyTypedInfo; #[derive(Debug, Clone, PartialEq, Eq)] @@ -69,11 +69,11 @@ pub(crate) struct ImportResult { /// A map from file to resolved path, for all implicitly imported /// modules that are part of a namespace package. - pub(crate) implicit_imports: HashMap, + pub(crate) implicit_imports: BTreeMap, /// Any implicit imports whose symbols were explicitly imported (i.e., via /// a `from x import y` statement). - pub(crate) filtered_implicit_imports: HashMap, + pub(crate) filtered_implicit_imports: BTreeMap, /// If the import resolved to a type hint (i.e., a `.pyi` file), then /// a non-type-hint resolution will be stored here. @@ -105,8 +105,8 @@ impl ImportResult { is_stdlib_typeshed_file: false, is_third_party_typeshed_file: false, is_local_typings_file: false, - implicit_imports: HashMap::default(), - filtered_implicit_imports: HashMap::default(), + implicit_imports: BTreeMap::default(), + filtered_implicit_imports: BTreeMap::default(), non_stub_import_result: None, py_typed_info: None, package_directory: None, diff --git a/crates/ruff_python_resolver/src/lib.rs b/crates/ruff_python_resolver/src/lib.rs index c1fef3be66..9cc1458b1f 100644 --- a/crates/ruff_python_resolver/src/lib.rs +++ b/crates/ruff_python_resolver/src/lib.rs @@ -12,5 +12,3 @@ mod python_platform; mod python_version; mod resolver; mod search; - -pub(crate) const SITE_PACKAGES: &str = "site-packages"; diff --git a/crates/ruff_python_resolver/src/resolver.rs b/crates/ruff_python_resolver/src/resolver.rs index 93e913a3f8..ee845f6839 100644 --- a/crates/ruff_python_resolver/src/resolver.rs +++ b/crates/ruff_python_resolver/src/resolver.rs @@ -1,6 +1,6 @@ //! Resolves Python imports to their corresponding files on disk. -use std::collections::HashMap; +use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use log::debug; @@ -36,7 +36,7 @@ fn resolve_module_descriptor( let mut is_stub_package = false; let mut is_stub_file = false; let mut is_native_lib = false; - let mut implicit_imports = HashMap::new(); + let mut implicit_imports = BTreeMap::new(); let mut package_directory = None; let mut py_typed_info = None; @@ -194,7 +194,7 @@ fn resolve_module_descriptor( is_third_party_typeshed_file: false, is_local_typings_file: false, implicit_imports, - filtered_implicit_imports: HashMap::default(), + filtered_implicit_imports: BTreeMap::default(), non_stub_import_result: None, py_typed_info, package_directory, @@ -424,7 +424,7 @@ fn resolve_best_absolute_import( /// are all satisfied by submodules (as listed in the implicit imports). fn is_namespace_package_resolved( module_descriptor: &ImportModuleDescriptor, - implicit_imports: &HashMap, + implicit_imports: &BTreeMap, ) -> bool { if !module_descriptor.imported_symbols.is_empty() { // Pyright uses `!Array.from(moduleDescriptor.importedSymbols.keys()).some((symbol) => implicitImports.has(symbol))`. @@ -774,6 +774,7 @@ fn resolve_import( #[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}; @@ -823,6 +824,8 @@ mod tests { library: Option, stub_path: Option, typeshed_path: Option, + venv_path: Option, + venv: Option, } fn resolve_options( @@ -836,6 +839,8 @@ mod tests { library, stub_path, typeshed_path, + venv_path, + venv, } = options; let execution_environment = ExecutionEnvironment { @@ -860,8 +865,8 @@ mod tests { let config = Config { typeshed_path, stub_path, - venv_path: None, - venv: None, + venv_path, + venv, }; let host = host::StaticHost::new(if let Some(library) = library { @@ -1545,4 +1550,109 @@ mod tests { 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/search.rs b/crates/ruff_python_resolver/src/search.rs index 9f61b350f0..57128e6458 100644 --- a/crates/ruff_python_resolver/src/search.rs +++ b/crates/ruff_python_resolver/src/search.rs @@ -8,9 +8,11 @@ use std::path::{Path, PathBuf}; use log::debug; use crate::config::Config; +use crate::host; use crate::module_descriptor::ImportModuleDescriptor; use crate::python_version::PythonVersion; -use crate::{host, SITE_PACKAGES}; + +const SITE_PACKAGES: &str = "site-packages"; /// Find the `site-packages` directory for the specified Python version. fn find_site_packages_path( 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__resolver__tests__airflow_first_party.snap new file mode 100644 index 0000000000..d2197f6cab --- /dev/null +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_first_party.snap @@ -0,0 +1,33 @@ +--- +source: crates/ruff_python_resolver/src/resolver.rs +expression: result +--- +ImportResult { + is_relative: false, + is_import_found: true, + is_partly_resolved: false, + is_namespace_package: false, + is_init_file_present: true, + is_stub_package: false, + import_type: Local, + resolved_paths: [ + "./resources/test/airflow/airflow/__init__.py", + "./resources/test/airflow/airflow/jobs/__init__.py", + "./resources/test/airflow/airflow/jobs/scheduler_job_runner.py", + ], + search_path: Some( + "./resources/test/airflow", + ), + is_stub_file: false, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: {}, + filtered_implicit_imports: {}, + non_stub_import_result: None, + py_typed_info: None, + package_directory: Some( + "./resources/test/airflow/airflow", + ), +} 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__resolver__tests__airflow_namespace_package.snap new file mode 100644 index 0000000000..15954d4c98 --- /dev/null +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_namespace_package.snap @@ -0,0 +1,36 @@ +--- +source: crates/ruff_python_resolver/src/resolver.rs +expression: result +--- +ImportResult { + is_relative: false, + is_import_found: true, + is_partly_resolved: false, + is_namespace_package: true, + is_init_file_present: true, + is_stub_package: false, + import_type: Local, + resolved_paths: [ + "./resources/test/airflow/airflow/__init__.py", + "", + "./resources/test/airflow/airflow/providers/google/__init__.py", + "./resources/test/airflow/airflow/providers/google/cloud/__init__.py", + "./resources/test/airflow/airflow/providers/google/cloud/hooks/__init__.py", + "./resources/test/airflow/airflow/providers/google/cloud/hooks/gcs.py", + ], + search_path: Some( + "./resources/test/airflow", + ), + is_stub_file: false, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: {}, + filtered_implicit_imports: {}, + non_stub_import_result: None, + py_typed_info: None, + package_directory: Some( + "./resources/test/airflow/airflow", + ), +} 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__resolver__tests__airflow_standard_library.snap new file mode 100644 index 0000000000..3262ce7f6f --- /dev/null +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_standard_library.snap @@ -0,0 +1,25 @@ +--- +source: crates/ruff_python_resolver/src/resolver.rs +expression: result +--- +ImportResult { + is_relative: false, + is_import_found: false, + is_partly_resolved: false, + is_namespace_package: false, + is_init_file_present: false, + is_stub_package: false, + import_type: Local, + resolved_paths: [], + search_path: None, + is_stub_file: false, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: {}, + filtered_implicit_imports: {}, + non_stub_import_result: None, + py_typed_info: None, + package_directory: None, +} 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__resolver__tests__airflow_stub_file.snap new file mode 100644 index 0000000000..37f26c80d7 --- /dev/null +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_stub_file.snap @@ -0,0 +1,63 @@ +--- +source: crates/ruff_python_resolver/src/resolver.rs +expression: result +--- +ImportResult { + is_relative: false, + is_import_found: true, + is_partly_resolved: false, + is_namespace_package: false, + is_init_file_present: true, + is_stub_package: false, + import_type: Local, + resolved_paths: [ + "./resources/test/airflow/airflow/__init__.py", + "./resources/test/airflow/airflow/compat/__init__.py", + "./resources/test/airflow/airflow/compat/functools.pyi", + ], + search_path: Some( + "./resources/test/airflow", + ), + is_stub_file: true, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: {}, + filtered_implicit_imports: {}, + non_stub_import_result: Some( + ImportResult { + is_relative: false, + is_import_found: true, + is_partly_resolved: false, + is_namespace_package: false, + is_init_file_present: true, + is_stub_package: false, + import_type: Local, + resolved_paths: [ + "./resources/test/airflow/airflow/__init__.py", + "./resources/test/airflow/airflow/compat/__init__.py", + "./resources/test/airflow/airflow/compat/functools.py", + ], + search_path: Some( + "./resources/test/airflow", + ), + is_stub_file: false, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: {}, + filtered_implicit_imports: {}, + non_stub_import_result: None, + py_typed_info: None, + package_directory: Some( + "./resources/test/airflow/airflow", + ), + }, + ), + py_typed_info: None, + package_directory: Some( + "./resources/test/airflow/airflow", + ), +} 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__resolver__tests__airflow_third_party.snap new file mode 100644 index 0000000000..04c953624f --- /dev/null +++ b/crates/ruff_python_resolver/src/snapshots/ruff_python_resolver__resolver__tests__airflow_third_party.snap @@ -0,0 +1,54 @@ +--- +source: crates/ruff_python_resolver/src/resolver.rs +expression: result +--- +ImportResult { + is_relative: false, + is_import_found: true, + is_partly_resolved: false, + is_namespace_package: false, + is_init_file_present: true, + is_stub_package: false, + import_type: ThirdParty, + resolved_paths: [ + "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy/__init__.py", + "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy/orm/__init__.py", + ], + search_path: Some( + "./resources/test/airflow/venv/Lib/python3.11/site-packages", + ), + is_stub_file: false, + is_native_lib: false, + is_stdlib_typeshed_file: false, + is_third_party_typeshed_file: false, + is_local_typings_file: false, + implicit_imports: { + "base": ImplicitImport { + is_stub_file: false, + is_native_lib: false, + name: "base", + path: "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy/orm/base.py", + py_typed: None, + }, + "dependency": ImplicitImport { + is_stub_file: false, + is_native_lib: false, + name: "dependency", + path: "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy/orm/dependency.py", + py_typed: None, + }, + "query": ImplicitImport { + is_stub_file: false, + is_native_lib: false, + name: "query", + path: "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy/orm/query.py", + py_typed: None, + }, + }, + filtered_implicit_imports: {}, + non_stub_import_result: None, + py_typed_info: None, + package_directory: Some( + "./resources/test/airflow/venv/Lib/python3.11/site-packages/sqlalchemy", + ), +}