ruff/crates/ruff_python_resolver/src/lib.rs
renovate[bot] a192d96880
Update pre-commit dependencies (#17073)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[abravalheri/validate-pyproject](https://redirect.github.com/abravalheri/validate-pyproject)
| repository | patch | `v0.24` -> `v0.24.1` |
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.11.0` -> `v0.11.2` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.30.2` -> `v1.31.0` |
|
[python-jsonschema/check-jsonschema](https://redirect.github.com/python-jsonschema/check-jsonschema)
| repository | minor | `0.31.3` -> `0.32.1` |
|
[woodruffw/zizmor-pre-commit](https://redirect.github.com/woodruffw/zizmor-pre-commit)
| repository | patch | `v1.5.1` -> `v1.5.2` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>abravalheri/validate-pyproject
(abravalheri/validate-pyproject)</summary>

###
[`v0.24.1`](https://redirect.github.com/abravalheri/validate-pyproject/releases/tag/v0.24.1)

[Compare
Source](https://redirect.github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1)

#### What's Changed

- Fixed multi plugin id was read from the wrong place by
[@&#8203;henryiii](https://redirect.github.com/henryiii) in
[https://github.com/abravalheri/validate-pyproject/pull/240](https://redirect.github.com/abravalheri/validate-pyproject/pull/240)
- Implemented alternative plugin sorting,
[https://github.com/abravalheri/validate-pyproject/pull/243](https://redirect.github.com/abravalheri/validate-pyproject/pull/243)

**Full Changelog**:
https://github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1

</details>

<details>
<summary>astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit)</summary>

###
[`v0.11.2`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.11.2)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.11.1...v0.11.2)

See: https://github.com/astral-sh/ruff/releases/tag/0.11.2

###
[`v0.11.1`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.11.1)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.1)

See: https://github.com/astral-sh/ruff/releases/tag/0.11.1

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.31.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.31.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.30.3...v1.31.0)

#### \[1.31.0] - 2025-03-28

##### Features

- Updated the dictionary with the [March
2025](https://redirect.github.com/crate-ci/typos/issues/1266) changes

###
[`v1.30.3`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.30.3)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.30.2...v1.30.3)

#### \[1.30.3] - 2025-03-24

##### Features

-   Support detecting `go.work` and `go.work.sum` files

</details>

<details>
<summary>python-jsonschema/check-jsonschema
(python-jsonschema/check-jsonschema)</summary>

###
[`v0.32.1`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0321)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.32.0...0.32.1)

-   Fix the `check-meltano` hook to use `types_or`. Thanks
    :user:`edgarrmondragon`! (:pr:`543`)

###
[`v0.32.0`](https://redirect.github.com/python-jsonschema/check-jsonschema/blob/HEAD/CHANGELOG.rst#0320)

[Compare
Source](https://redirect.github.com/python-jsonschema/check-jsonschema/compare/0.31.3...0.32.0)

- Update vendored schemas: circle-ci, compose-spec, dependabot,
github-workflows,
    gitlab-ci, mergify, renovate, taskfile (2025-03-25)
- Add Meltano schema and pre-commit hook. Thanks
:user:`edgarrmondragon`! (:issue:`540`)
- Add Snapcraft schema and pre-commit hook. Thanks :user:`fabolhak`!
(:issue:`535`)

</details>

<details>
<summary>woodruffw/zizmor-pre-commit
(woodruffw/zizmor-pre-commit)</summary>

###
[`v1.5.2`](https://redirect.github.com/woodruffw/zizmor-pre-commit/releases/tag/v1.5.2)

[Compare
Source](https://redirect.github.com/woodruffw/zizmor-pre-commit/compare/v1.5.1...v1.5.2)

See: https://github.com/woodruffw/zizmor/releases/tag/v1.5.2

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMDcuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIwNy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-31 07:42:15 +00:00

948 lines
27 KiB
Rust

#![allow(dead_code)]
mod config;
mod execution_environment;
mod host;
mod implicit_imports;
mod import_result;
mod module_descriptor;
mod native_module;
mod py_typed;
mod python_platform;
mod python_version;
mod resolver;
mod search;
#[cfg(test)]
mod tests {
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();
}
macro_rules! assert_debug_snapshot_normalize_paths {
($value: ident) => {{
// The debug representation for the backslash are two backslashes (escaping)
let $value = std::format!("{:#?}", $value).replace("\\\\", "/");
insta::assert_snapshot!($value);
}};
}
#[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_normalize_paths!(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_normalize_paths!(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_normalize_paths!(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_normalize_paths!(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_normalize_paths!(result);
}
#[test]
fn airflow_explicit_native_module() {
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,
"_watchdog_fsevents",
root.clone(),
ResolverOptions {
venv_path: Some(root),
venv: Some(PathBuf::from("venv")),
..Default::default()
},
);
assert_debug_snapshot_normalize_paths!(result);
}
#[test]
fn airflow_implicit_native_module() {
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,
"orjson",
root.clone(),
ResolverOptions {
venv_path: Some(root),
venv: Some(PathBuf::from("venv")),
..Default::default()
},
);
assert_debug_snapshot_normalize_paths!(result);
}
}