[ty] Support import <namespace> and from <namespace> import module (#18137)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Micha Reiser 2025-05-21 09:28:33 +02:00 committed by GitHub
parent 7b253100f8
commit 76ab77fe01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 359 additions and 158 deletions

View file

@ -14,15 +14,16 @@ pub struct Module {
}
impl Module {
pub(crate) fn new(
pub(crate) fn file_module(
name: ModuleName,
kind: ModuleKind,
search_path: SearchPath,
file: File,
) -> Self {
let known = KnownModule::try_from_search_path_and_name(&search_path, &name);
Self {
inner: Arc::new(ModuleInner {
inner: Arc::new(ModuleInner::FileModule {
name,
kind,
search_path,
@ -32,19 +33,36 @@ impl Module {
}
}
pub(crate) fn namespace_package(name: ModuleName) -> Self {
Self {
inner: Arc::new(ModuleInner::NamespacePackage { name }),
}
}
/// The absolute name of the module (e.g. `foo.bar`)
pub fn name(&self) -> &ModuleName {
&self.inner.name
match &*self.inner {
ModuleInner::FileModule { name, .. } => name,
ModuleInner::NamespacePackage { name, .. } => name,
}
}
/// The file to the source code that defines this module
pub fn file(&self) -> File {
self.inner.file
///
/// This is `None` for namespace packages.
pub fn file(&self) -> Option<File> {
match &*self.inner {
ModuleInner::FileModule { file, .. } => Some(*file),
ModuleInner::NamespacePackage { .. } => None,
}
}
/// Is this a module that we special-case somehow? If so, which one?
pub fn known(&self) -> Option<KnownModule> {
self.inner.known
match &*self.inner {
ModuleInner::FileModule { known, .. } => *known,
ModuleInner::NamespacePackage { .. } => None,
}
}
/// Does this module represent the given known module?
@ -53,13 +71,19 @@ impl Module {
}
/// The search path from which the module was resolved.
pub(crate) fn search_path(&self) -> &SearchPath {
&self.inner.search_path
pub(crate) fn search_path(&self) -> Option<&SearchPath> {
match &*self.inner {
ModuleInner::FileModule { search_path, .. } => Some(search_path),
ModuleInner::NamespacePackage { .. } => None,
}
}
/// Determine whether this module is a single-file module or a package
pub fn kind(&self) -> ModuleKind {
self.inner.kind
match &*self.inner {
ModuleInner::FileModule { kind, .. } => *kind,
ModuleInner::NamespacePackage { .. } => ModuleKind::Package,
}
}
}
@ -70,17 +94,26 @@ impl std::fmt::Debug for Module {
.field("kind", &self.kind())
.field("file", &self.file())
.field("search_path", &self.search_path())
.field("known", &self.known())
.finish()
}
}
#[derive(PartialEq, Eq, Hash)]
struct ModuleInner {
name: ModuleName,
kind: ModuleKind,
search_path: SearchPath,
file: File,
known: Option<KnownModule>,
enum ModuleInner {
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
FileModule {
name: ModuleName,
kind: ModuleKind,
search_path: SearchPath,
file: File,
known: Option<KnownModule>,
},
/// A namespace package. Namespace packages are special because
/// there are multiple possible paths and they have no corresponding
/// code file.
NamespacePackage { name: ModuleName },
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]

View file

@ -143,6 +143,23 @@ impl ModulePath {
}
}
pub(super) fn to_system_path(&self) -> Option<SystemPathBuf> {
let ModulePath {
search_path,
relative_path,
} = self;
match &*search_path.0 {
SearchPathInner::Extra(search_path)
| SearchPathInner::FirstParty(search_path)
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => Some(search_path.join(relative_path)),
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
Some(stdlib_root.join(relative_path))
}
SearchPathInner::StandardLibraryVendored(_) => None,
}
}
#[must_use]
pub(super) fn to_file(&self, resolver: &ResolverContext) -> Option<File> {
let db = resolver.db.upcast();

View file

@ -39,22 +39,24 @@ pub(crate) fn resolve_module_query<'db>(
let name = module_name.name(db);
let _span = tracing::trace_span!("resolve_module", %name).entered();
let Some((search_path, resolved_module)) = resolve_name(db, name) else {
let Some(resolved) = resolve_name(db, name) else {
tracing::debug!("Module `{name}` not found in search paths");
return None;
};
tracing::trace!(
"Resolved module `{name}` to `{path}`",
path = resolved_module.file.path(db)
);
let module = Module::new(
name.clone(),
resolved_module.kind,
search_path,
resolved_module.file,
);
let module = match resolved {
ResolvedName::FileModule(module) => {
tracing::trace!(
"Resolved module `{name}` to `{path}`",
path = module.file.path(db)
);
Module::file_module(name.clone(), module.kind, module.search_path, module.file)
}
ResolvedName::NamespacePackage => {
tracing::trace!("Module `{name}` is a namespace package");
Module::namespace_package(name.clone())
}
};
Some(module)
}
@ -117,8 +119,9 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
// root paths, but that the module corresponding to `path` is in a lower priority search path,
// in which case we ignore it.
let module = resolve_module(db, &module_name)?;
let module_file = module.file()?;
if file.path(db) == module.file().path(db) {
if file.path(db) == module_file.path(db) {
Some(module)
} else {
// This path is for a module with the same name but with a different precedence. For example:
@ -616,7 +619,7 @@ struct ModuleNameIngredient<'db> {
/// Given a module name and a list of search paths in which to lookup modules,
/// attempt to resolve the module name
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedModule)> {
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<ResolvedName> {
let program = Program::get(db);
let python_version = program.python_version(db);
let resolver_state = ResolverContext::new(db, python_version);
@ -625,6 +628,7 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
let name = RelaxedModuleName::new(name);
let stub_name = name.to_stub_package();
let mut is_namespace_package = false;
for search_path in search_paths(db) {
// When a builtin module is imported, standard module resolution is bypassed:
@ -636,16 +640,19 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
}
if !search_path.is_standard_library() {
match resolve_module_in_search_path(&resolver_state, &stub_name, search_path) {
Ok(resolved_module) => {
if resolved_module.package_kind.is_root() && resolved_module.kind.is_module() {
match resolve_name_in_search_path(&resolver_state, &stub_name, search_path) {
Ok((package_kind, ResolvedName::FileModule(module))) => {
if package_kind.is_root() && module.kind.is_module() {
tracing::trace!(
"Search path '{search_path} contains a module named `{stub_name}` but a standalone module isn't a valid stub."
);
} else {
return Some((search_path.clone(), resolved_module));
return Some(ResolvedName::FileModule(module));
}
}
Ok((_, ResolvedName::NamespacePackage)) => {
is_namespace_package = true;
}
Err(PackageKind::Root) => {
tracing::trace!(
"Search path '{search_path}' contains no stub package named `{stub_name}`."
@ -670,8 +677,13 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
}
}
match resolve_module_in_search_path(&resolver_state, &name, search_path) {
Ok(resolved_module) => return Some((search_path.clone(), resolved_module)),
match resolve_name_in_search_path(&resolver_state, &name, search_path) {
Ok((_, ResolvedName::FileModule(module))) => {
return Some(ResolvedName::FileModule(module));
}
Ok((_, ResolvedName::NamespacePackage)) => {
is_namespace_package = true;
}
Err(kind) => match kind {
PackageKind::Root => {
tracing::trace!(
@ -693,21 +705,36 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
}
}
if is_namespace_package {
return Some(ResolvedName::NamespacePackage);
}
None
}
#[derive(Debug)]
struct ResolvedModule {
enum ResolvedName {
/// A module that resolves to a file.
FileModule(ResolvedFileModule),
/// The module name resolved to a namespace package.
///
/// For example, `from opentelemetry import trace, metrics` where `opentelemetry` is a namespace package (and `trace` and `metrics` are sub packages).
NamespacePackage,
}
#[derive(Debug)]
struct ResolvedFileModule {
kind: ModuleKind,
package_kind: PackageKind,
search_path: SearchPath,
file: File,
}
fn resolve_module_in_search_path(
fn resolve_name_in_search_path(
context: &ResolverContext,
name: &RelaxedModuleName,
search_path: &SearchPath,
) -> Result<ResolvedModule, PackageKind> {
) -> Result<(PackageKind, ResolvedName), PackageKind> {
let mut components = name.components();
let module_name = components.next_back().unwrap();
@ -720,22 +747,55 @@ fn resolve_module_in_search_path(
// Check for a regular package first (highest priority)
package_path.push("__init__");
if let Some(regular_package) = resolve_file_module(&package_path, context) {
return Ok(ResolvedModule {
file: regular_package,
kind: ModuleKind::Package,
package_kind: resolved_package.kind,
});
return Ok((
resolved_package.kind,
ResolvedName::FileModule(ResolvedFileModule {
search_path: search_path.clone(),
kind: ModuleKind::Package,
file: regular_package,
}),
));
}
// Check for a file module next
package_path.pop();
if let Some(file_module) = resolve_file_module(&package_path, context) {
return Ok(ResolvedModule {
file: file_module,
kind: ModuleKind::Module,
package_kind: resolved_package.kind,
});
return Ok((
resolved_package.kind,
ResolvedName::FileModule(ResolvedFileModule {
file: file_module,
kind: ModuleKind::Module,
search_path: search_path.clone(),
}),
));
}
// Last resort, check if a folder with the given name exists.
// If so, then this is a namespace package.
// We need to skip this check for typeshed because the `resolve_file_module` can also return `None`
// if the `__init__.py` exists but isn't available for the current Python version.
// Let's assume that the `xml` module is only available on Python 3.11+ and we're resolving for Python 3.10:
// * `resolve_file_module("xml/__init__.pyi")` returns `None` even though the file exists but the
// module isn't available for the current Python version.
// * The check here would now return `true` because the `xml` directory exists, resulting
// in a false positive for a namespace package.
//
// Since typeshed doesn't use any namespace packages today (May 2025), simply skip this
// check which also helps performance. If typeshed ever uses namespace packages, ensure that
// this check also takes the `VERSIONS` file into consideration.
if !search_path.is_standard_library() && package_path.is_directory(context) {
if let Some(path) = package_path.to_system_path() {
let system = context.db.system();
if system.case_sensitivity().is_case_sensitive()
|| system.path_exists_case_sensitive(
&path,
package_path.search_path().as_system_path().unwrap(),
)
{
return Ok((resolved_package.kind, ResolvedName::NamespacePackage));
}
}
}
Err(resolved_package.kind)
@ -937,11 +997,11 @@ mod tests {
);
assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path());
assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, foo_module.kind());
let expected_foo_path = src.join("foo.py");
assert_eq!(&expected_foo_path, foo_module.file().path(&db));
assert_eq!(&expected_foo_path, foo_module.file().unwrap().path(&db));
assert_eq!(
Some(foo_module),
path_to_module(&db, &FilePath::System(expected_foo_path))
@ -958,7 +1018,10 @@ mod tests {
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
assert_eq!(
builtins.file().unwrap().path(&db),
&stdlib.join("builtins.pyi")
);
}
#[test]
@ -979,7 +1042,10 @@ mod tests {
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
assert_eq!(
builtins.file().unwrap().path(&db),
&stdlib.join("builtins.pyi")
);
}
#[test]
@ -1002,11 +1068,14 @@ mod tests {
resolve_module(&db, &functools_module_name).as_ref()
);
assert_eq!(&stdlib, functools_module.search_path());
assert_eq!(&stdlib, functools_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, functools_module.kind());
let expected_functools_path = stdlib.join("functools.pyi");
assert_eq!(&expected_functools_path, functools_module.file().path(&db));
assert_eq!(
&expected_functools_path,
functools_module.file().unwrap().path(&db)
);
assert_eq!(
Some(functools_module),
@ -1052,7 +1121,7 @@ mod tests {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path();
let search_path = resolved_module.search_path().unwrap();
assert_eq!(
&stdlib, search_path,
"Search path for {module_name} was unexpectedly {search_path:?}"
@ -1148,7 +1217,7 @@ mod tests {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path();
let search_path = resolved_module.search_path().unwrap();
assert_eq!(
&stdlib, search_path,
"Search path for {module_name} was unexpectedly {search_path:?}"
@ -1209,9 +1278,12 @@ mod tests {
Some(&functools_module),
resolve_module(&db, &functools_module_name).as_ref()
);
assert_eq!(&src, functools_module.search_path());
assert_eq!(&src, functools_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, functools_module.kind());
assert_eq!(&src.join("functools.py"), functools_module.file().path(&db));
assert_eq!(
&src.join("functools.py"),
functools_module.file().unwrap().path(&db)
);
assert_eq!(
Some(functools_module),
@ -1230,9 +1302,9 @@ mod tests {
let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap();
assert_eq!("pydoc_data.topics", pydoc_data_topics.name());
assert_eq!(pydoc_data_topics.search_path(), &stdlib);
assert_eq!(pydoc_data_topics.search_path().unwrap(), &stdlib);
assert_eq!(
pydoc_data_topics.file().path(&db),
pydoc_data_topics.file().unwrap().path(&db),
&stdlib.join("pydoc_data/topics.pyi")
);
}
@ -1247,8 +1319,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path());
assert_eq!(&foo_path, foo_module.file().path(&db));
assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_path, foo_module.file().unwrap().path(&db));
assert_eq!(
Some(&foo_module),
@ -1274,8 +1346,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&src, foo_module.search_path());
assert_eq!(&foo_init_path, foo_module.file().path(&db));
assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_init_path, foo_module.file().unwrap().path(&db));
assert_eq!(ModuleKind::Package, foo_module.kind());
assert_eq!(
@ -1297,8 +1369,8 @@ mod tests {
let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_stub = src.join("foo.pyi");
assert_eq!(&src, foo.search_path());
assert_eq!(&foo_stub, foo.file().path(&db));
assert_eq!(&src, foo.search_path().unwrap());
assert_eq!(&foo_stub, foo.file().unwrap().path(&db));
assert_eq!(Some(foo), path_to_module(&db, &FilePath::System(foo_stub)));
assert_eq!(
@ -1321,8 +1393,8 @@ mod tests {
resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
let baz_path = src.join("foo/bar/baz.py");
assert_eq!(&src, baz_module.search_path());
assert_eq!(&baz_path, baz_module.file().path(&db));
assert_eq!(&src, baz_module.search_path().unwrap());
assert_eq!(&baz_path, baz_module.file().unwrap().path(&db));
assert_eq!(
Some(baz_module),
@ -1345,8 +1417,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_src_path = src.join("foo.py");
assert_eq!(&src, foo_module.search_path());
assert_eq!(&foo_src_path, foo_module.file().path(&db));
assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_src_path, foo_module.file().unwrap().path(&db));
assert_eq!(
Some(foo_module),
path_to_module(&db, &FilePath::System(foo_src_path))
@ -1413,14 +1485,14 @@ mod tests {
assert_ne!(foo_module, bar_module);
assert_eq!(&src, foo_module.search_path());
assert_eq!(&foo, foo_module.file().path(&db));
assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo, foo_module.file().unwrap().path(&db));
// `foo` and `bar` shouldn't resolve to the same file
assert_eq!(&src, bar_module.search_path());
assert_eq!(&bar, bar_module.file().path(&db));
assert_eq!(&foo, foo_module.file().path(&db));
assert_eq!(&src, bar_module.search_path().unwrap());
assert_eq!(&bar, bar_module.file().unwrap().path(&db));
assert_eq!(&foo, foo_module.file().unwrap().path(&db));
assert_ne!(&foo_module, &bar_module);
@ -1484,7 +1556,7 @@ mod tests {
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(foo_file, foo_module.file());
assert_eq!(foo_file, foo_module.file().unwrap());
Ok(())
}
@ -1500,7 +1572,7 @@ mod tests {
let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist");
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&foo_init_path, foo_module.file().path(&db));
assert_eq!(&foo_init_path, foo_module.file().unwrap().path(&db));
// Delete `foo/__init__.py` and the `foo` folder. `foo` should now resolve to `foo.py`
db.memory_file_system().remove_file(&foo_init_path)?;
@ -1510,7 +1582,7 @@ mod tests {
File::sync_path(&mut db, foo_init_path.parent().unwrap());
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
assert_eq!(&src.join("foo.py"), foo_module.file().unwrap().path(&db));
Ok(())
}
@ -1536,9 +1608,9 @@ mod tests {
let stdlib_functools_path = stdlib.join("functools.pyi");
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &stdlib_functools_path)
);
@ -1556,9 +1628,9 @@ mod tests {
ModuleNameIngredient::new(&db, functools_module_name),
&events,
);
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &stdlib_functools_path)
);
}
@ -1582,9 +1654,9 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, stdlib.join("functools.pyi"))
);
@ -1593,9 +1665,9 @@ mod tests {
let src_functools_path = src.join("functools.py");
db.write_file(&src_functools_path, "FOO: int").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(functools_module.search_path().unwrap(), &src);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &src_functools_path)
);
}
@ -1624,9 +1696,9 @@ mod tests {
let src_functools_path = src.join("functools.py");
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(functools_module.search_path().unwrap(), &src);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &src_functools_path)
);
@ -1637,9 +1709,9 @@ mod tests {
.unwrap();
File::sync_path(&mut db, &src_functools_path);
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
Ok(functools_module.file().unwrap()),
system_path_to_file(&db, stdlib.join("functools.pyi"))
);
}
@ -1662,11 +1734,11 @@ mod tests {
let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo/__init__.py")
);
assert_eq!(
foo_bar_module.file().path(&db),
foo_bar_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo/bar.py")
);
}
@ -1693,7 +1765,7 @@ mod tests {
let bar_module_name = ModuleName::new_static("bar").unwrap();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file().path(&db),
bar_module.file().unwrap().path(&db),
&FilePath::system("/y/src/bar.py")
);
}
@ -1713,7 +1785,7 @@ mod tests {
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::system("/x/y/src/foo.pyi")
);
}
@ -1764,13 +1836,19 @@ not_a_directory
let spam_module = resolve_module(&db, &spam_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::system("/x/y/src/foo.pyi")
);
assert_eq!(a_module.file().path(&db), &FilePath::system("/a.py"));
assert_eq!(b_module.file().path(&db), &FilePath::system("/baz/b.py"));
assert_eq!(
spam_module.file().path(&db),
a_module.file().unwrap().path(&db),
&FilePath::system("/a.py")
);
assert_eq!(
b_module.file().unwrap().path(&db),
&FilePath::system("/baz/b.py")
);
assert_eq!(
spam_module.file().unwrap().path(&db),
&FilePath::System(site_packages.join("spam/spam.py"))
);
}
@ -1791,14 +1869,14 @@ not_a_directory
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo.py")
);
db.clear_salsa_events();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file().path(&db),
bar_module.file().unwrap().path(&db),
&FilePath::system("/y/src/bar.py")
);
let events = db.take_salsa_events();
@ -1823,7 +1901,7 @@ not_a_directory
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo.py")
);
@ -1851,7 +1929,7 @@ not_a_directory
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let src_path = SystemPathBuf::from("/x/src");
assert_eq!(
foo_module.file().path(&db),
foo_module.file().unwrap().path(&db),
&FilePath::System(src_path.join("foo.py"))
);
@ -1925,7 +2003,10 @@ not_a_directory
// take precedence over the second `site-packages` directory...
let a_module_name = ModuleName::new_static("a").unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &editable_install_location);
assert_eq!(
a_module.file().unwrap().path(&db),
&editable_install_location
);
db.memory_file_system()
.remove_file(&site_packages_pth)
@ -1936,7 +2017,10 @@ not_a_directory
// the editable install no longer exists, so the module now resolves to the file in the
// second `site-packages` directory
let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &system_site_packages_location);
assert_eq!(
a_module.file().unwrap().path(&db),
&system_site_packages_location
);
}
#[test]
@ -2001,6 +2085,7 @@ not_a_directory
assert!(
a_module
.file()
.unwrap()
.path(&db)
.as_str()
.ends_with("src/a/__init__.py"),
@ -2035,6 +2120,6 @@ not_a_directory
let foo_module_file = File::new(&db, FilePath::System(installed_foo_module));
let module = file_to_module(&db, foo_module_file).unwrap();
assert_eq!(module.search_path(), &site_packages);
assert_eq!(module.search_path().unwrap(), &site_packages);
}
}