Skip to content

Commit 3d4b055

Browse files
authored
[ty] remove erroneous canonicalize (#21405)
Alternative implementation to #21052
1 parent 2f6f3e1 commit 3d4b055

File tree

2 files changed

+228
-6
lines changed

2 files changed

+228
-6
lines changed

crates/ty/tests/cli/python_environment.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,231 @@ fn python_version_inferred_from_system_installation() -> anyhow::Result<()> {
323323
Ok(())
324324
}
325325

326+
/// This attempts to simulate the tangled web of symlinks that a homebrew install has
327+
/// which can easily confuse us if we're ever told to use it.
328+
///
329+
/// The main thing this is regression-testing is a panic in one *extremely* specific case
330+
/// that you have to try really hard to hit (but vscode, hilariously, did hit).
331+
#[cfg(unix)]
332+
#[test]
333+
fn python_argument_trapped_in_a_symlink_factory() -> anyhow::Result<()> {
334+
let case = CliTest::with_files([
335+
// This is the real python binary.
336+
(
337+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3.13",
338+
"",
339+
),
340+
// There's a real site-packages here (although it's basically empty).
341+
(
342+
"opt/homebrew/Cellar/python@3.13/3.13.5/lib/python3.13/site-packages/foo.py",
343+
"",
344+
),
345+
// There's also a real site-packages here (although it's basically empty).
346+
("opt/homebrew/lib/python3.13/site-packages/bar.py", ""),
347+
// This has the real stdlib, but the site-packages in this dir is a symlink.
348+
(
349+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/abc.py",
350+
"",
351+
),
352+
// It's important that this our faux-homebrew not be in the same dir as our working directory
353+
// to reproduce the crash, don't ask me why.
354+
(
355+
"project/test.py",
356+
"\
357+
import foo
358+
import bar
359+
import colorama
360+
",
361+
),
362+
])?;
363+
364+
// many python symlinks pointing to a single real python (the longest path)
365+
case.write_symlink(
366+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3.13",
367+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3",
368+
)?;
369+
case.write_symlink(
370+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3",
371+
"opt/homebrew/Cellar/python@3.13/3.13.5/bin/python3",
372+
)?;
373+
case.write_symlink(
374+
"opt/homebrew/Cellar/python@3.13/3.13.5/bin/python3",
375+
"opt/homebrew/bin/python3",
376+
)?;
377+
// the "real" python's site-packages is a symlink to a different dir
378+
case.write_symlink(
379+
"opt/homebrew/Cellar/python@3.13/3.13.5/lib/python3.13/site-packages",
380+
"opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages",
381+
)?;
382+
383+
// Try all 4 pythons with absolute paths to our fauxbrew install
384+
assert_cmd_snapshot!(case.command()
385+
.current_dir(case.root().join("project"))
386+
.arg("--python").arg(case.root().join("opt/homebrew/bin/python3")), @r"
387+
success: false
388+
exit_code: 1
389+
----- stdout -----
390+
error[unresolved-import]: Cannot resolve imported module `foo`
391+
--> test.py:1:8
392+
|
393+
1 | import foo
394+
| ^^^
395+
2 | import bar
396+
3 | import colorama
397+
|
398+
info: Searched in the following paths during module resolution:
399+
info: 1. <temp_dir>/project (first-party code)
400+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
401+
info: 3. <temp_dir>/opt/homebrew/lib/python3.13/site-packages (site-packages)
402+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
403+
info: rule `unresolved-import` is enabled by default
404+
405+
error[unresolved-import]: Cannot resolve imported module `colorama`
406+
--> test.py:3:8
407+
|
408+
1 | import foo
409+
2 | import bar
410+
3 | import colorama
411+
| ^^^^^^^^
412+
|
413+
info: Searched in the following paths during module resolution:
414+
info: 1. <temp_dir>/project (first-party code)
415+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
416+
info: 3. <temp_dir>/opt/homebrew/lib/python3.13/site-packages (site-packages)
417+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
418+
info: rule `unresolved-import` is enabled by default
419+
420+
Found 2 diagnostics
421+
422+
----- stderr -----
423+
");
424+
425+
assert_cmd_snapshot!(case.command()
426+
.current_dir(case.root().join("project"))
427+
.arg("--python").arg(case.root().join("opt/homebrew/Cellar/python@3.13/3.13.5/bin/python3")), @r"
428+
success: false
429+
exit_code: 1
430+
----- stdout -----
431+
error[unresolved-import]: Cannot resolve imported module `bar`
432+
--> test.py:2:8
433+
|
434+
1 | import foo
435+
2 | import bar
436+
| ^^^
437+
3 | import colorama
438+
|
439+
info: Searched in the following paths during module resolution:
440+
info: 1. <temp_dir>/project (first-party code)
441+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
442+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/lib/python3.13/site-packages (site-packages)
443+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
444+
info: rule `unresolved-import` is enabled by default
445+
446+
error[unresolved-import]: Cannot resolve imported module `colorama`
447+
--> test.py:3:8
448+
|
449+
1 | import foo
450+
2 | import bar
451+
3 | import colorama
452+
| ^^^^^^^^
453+
|
454+
info: Searched in the following paths during module resolution:
455+
info: 1. <temp_dir>/project (first-party code)
456+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
457+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/lib/python3.13/site-packages (site-packages)
458+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
459+
info: rule `unresolved-import` is enabled by default
460+
461+
Found 2 diagnostics
462+
463+
----- stderr -----
464+
");
465+
466+
assert_cmd_snapshot!(case.command()
467+
.current_dir(case.root().join("project"))
468+
.arg("--python").arg(case.root().join("opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3")), @r"
469+
success: false
470+
exit_code: 1
471+
----- stdout -----
472+
error[unresolved-import]: Cannot resolve imported module `bar`
473+
--> test.py:2:8
474+
|
475+
1 | import foo
476+
2 | import bar
477+
| ^^^
478+
3 | import colorama
479+
|
480+
info: Searched in the following paths during module resolution:
481+
info: 1. <temp_dir>/project (first-party code)
482+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
483+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages (site-packages)
484+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
485+
info: rule `unresolved-import` is enabled by default
486+
487+
error[unresolved-import]: Cannot resolve imported module `colorama`
488+
--> test.py:3:8
489+
|
490+
1 | import foo
491+
2 | import bar
492+
3 | import colorama
493+
| ^^^^^^^^
494+
|
495+
info: Searched in the following paths during module resolution:
496+
info: 1. <temp_dir>/project (first-party code)
497+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
498+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages (site-packages)
499+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
500+
info: rule `unresolved-import` is enabled by default
501+
502+
Found 2 diagnostics
503+
504+
----- stderr -----
505+
");
506+
507+
assert_cmd_snapshot!(case.command()
508+
.current_dir(case.root().join("project"))
509+
.arg("--python").arg(case.root().join("opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/bin/python3.13")), @r"
510+
success: false
511+
exit_code: 1
512+
----- stdout -----
513+
error[unresolved-import]: Cannot resolve imported module `bar`
514+
--> test.py:2:8
515+
|
516+
1 | import foo
517+
2 | import bar
518+
| ^^^
519+
3 | import colorama
520+
|
521+
info: Searched in the following paths during module resolution:
522+
info: 1. <temp_dir>/project (first-party code)
523+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
524+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages (site-packages)
525+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
526+
info: rule `unresolved-import` is enabled by default
527+
528+
error[unresolved-import]: Cannot resolve imported module `colorama`
529+
--> test.py:3:8
530+
|
531+
1 | import foo
532+
2 | import bar
533+
3 | import colorama
534+
| ^^^^^^^^
535+
|
536+
info: Searched in the following paths during module resolution:
537+
info: 1. <temp_dir>/project (first-party code)
538+
info: 2. vendored://stdlib (stdlib typeshed stubs vendored by ty)
539+
info: 3. <temp_dir>/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages (site-packages)
540+
info: make sure your Python environment is properly configured: https://docs.astral.sh/ty/modules/#python-environment
541+
info: rule `unresolved-import` is enabled by default
542+
543+
Found 2 diagnostics
544+
545+
----- stderr -----
546+
");
547+
548+
Ok(())
549+
}
550+
326551
/// On Unix systems, it's common for a Python installation at `.venv/bin/python` to only be a symlink
327552
/// to a system Python installation. We must be careful not to resolve the symlink too soon!
328553
/// If we do, we will incorrectly add the system installation's `site-packages` as a search path,

crates/ty_python_semantic/src/module_resolver/resolver.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -452,15 +452,12 @@ pub(crate) fn dynamic_resolution_paths<'db>(
452452
let site_packages_dir = site_packages_search_path
453453
.as_system_path()
454454
.expect("Expected site package path to be a system path");
455-
let site_packages_dir = system
456-
.canonicalize_path(site_packages_dir)
457-
.unwrap_or_else(|_| site_packages_dir.to_path_buf());
458455

459-
if !existing_paths.insert(Cow::Owned(site_packages_dir.clone())) {
456+
if !existing_paths.insert(Cow::Borrowed(site_packages_dir)) {
460457
continue;
461458
}
462459

463-
let site_packages_root = files.expect_root(db, &site_packages_dir);
460+
let site_packages_root = files.expect_root(db, site_packages_dir);
464461

465462
// This query needs to be re-executed each time a `.pth` file
466463
// is added, modified or removed from the `site-packages` directory.
@@ -477,7 +474,7 @@ pub(crate) fn dynamic_resolution_paths<'db>(
477474
// containing a (relative or absolute) path.
478475
// Each of these paths may point to an editable install of a package,
479476
// so should be considered an additional search path.
480-
let pth_file_iterator = match PthFileIterator::new(db, &site_packages_dir) {
477+
let pth_file_iterator = match PthFileIterator::new(db, site_packages_dir) {
481478
Ok(iterator) => iterator,
482479
Err(error) => {
483480
tracing::warn!(

0 commit comments

Comments
 (0)