facebook / facebook/pyrefly

False `missing-source` when stubs are on `search-path` and the source is in site-packages

Open
#4,439 0 comments 0 reactions 0 assignees View on GitHub
configuration
Dominant language
Rust
Stars
7k
Forks
519
PR merge metrics
No merged PRs in 30d

Description

pyrefly 1.2.0 · Windows 11 · CPython 3.14

If a PEP 561 stub-only package `foo-stubs/` is found on `search-path`, pyrefly does not
look in `site-package-path` for the runtime `foo/`, and reports `missing-source` even
though the package is installed and importable.

## Repro

`foo` is placed in a real venv's site-packages, so it is genuinely importable; the venv is
auto-detected, no `python-interpreter` setting needed.

```bash
mkdir repro && cd repro
python -m venv .venv
PY=$([ -x .venv/bin/python ] && echo .venv/bin/python || echo .venv/Scripts/python.exe)
SP=$("$PY" -c "import sysconfig;print(sysconfig.get_paths()['purelib'])")
mkdir -p "$SP/foo" foo-stubs
printf 'def f():\n return 1\n' > "$SP/foo/__init__.py"
printf 'def f() -> int: ...\n' > foo-stubs/__init__.pyi
printf 'import foo\n' > main.py
printf 'search-path = ["."]\nproject-includes = ["main.py"]\n' > pyrefly.toml
"$PY" -c "import foo; print('runtime import OK:', foo.__file__)"
pyrefly check --preset all
```

```powershell
mkdir repro; cd repro
python -m venv .venv
$PY = ".venv\Scripts\python.exe"
$SP = & $PY -c "import sysconfig;print(sysconfig.get_paths()['purelib'])"
New-Item -ItemType Directory -Force "$SP\foo", foo-stubs | Out-Null
@'
def f():
return 1
'@ | Set-Content "$SP\foo\__init__.py"
'def f() -> int: ...' | Set-Content foo-stubs\__init__.pyi
'import foo' | Set-Content main.py
@'
search-path = ["."]
project-includes = ["main.py"]
'@ | Set-Content pyrefly.toml
& $PY -c "import foo; print('runtime import OK:', foo.__file__)"
pyrefly check --preset all
```

The `import foo` line succeeds, printing the path inside `.venv`. pyrefly nevertheless
reports:

```
ERROR Found stubs for `foo`, but no source. This means it's likely not installed/unimportable. [missing-source]
--> main.py:1:8
|
1 | import foo
| ^^^
```

Expected: 0 errors.

## Where the lookup stops

Moving only the two packages (verified both with a real venv and with a plain directory
declared via `site-package-path`, which behave identically):

| Stubs in | Source in | Result |
|---|---|---|
| `search-path` | `site-package-path` | **error** |
| `search-path` | `site-package-path`, + `py.typed` | **error** |
| `search-path` | `search-path`, same root | ok |
| `site-package-path` | `site-package-path`, same root | ok |
| `search-path` root #1 | `search-path` root #2 | ok |
| `site-package-path` #1 | `site-package-path` #2 | ok |

The lookup spans multiple roots *within* a category; only the cross-category case fails.
`py.typed` makes no difference. Pure-`.py` and compiled `.pyd` submodules fail
identically, so this isn't extension-module handling — `.pyd` is correctly recognised as
source when it shares a root with its stub.

Contributor guide

Open the contributing guide

Research direction

Start with the provided real-venv reproduction using pyrefly.toml, main.py, and `pyrefly check --preset all`; compare lookup behavior across search-path and site-package-path. Trace the package resolution entry point for stub-only packages and make the cross-category case find the runtime source, while preserving the listed same-category cases. Done means the reproduction reports 0 errors on Windows and the other demonstrated layouts remain correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.