facebook / facebook/pyrefly

`explicit-any` is reported for `numpy.typing.NDArray` use sites even though `Any` comes from NumPy

Open
#4,088 4 comments 1 reaction 0 assignees View on GitHub
stale typechecking
Dominant language
Rust
Stars
7k
Forks
516
PR merge metrics
No merged PRs in 30d

Description

### Describe the Bug

## Summary

My company is interested in using Pyrefly's `explicit-any` error kind to catch first-party explicit `Any` usages, but currently Pyrefly reports `explicit-any` on third-party type aliases our first party code uses like `NDArray[np.float32]` from `numpy.typing`, even though our first party code does not write `Any` explicitly.

The `Any` appears to come from NumPy's typing internals or alias expansion. This makes common NumPy annotations noisy in first-party code.

For now we are disabling `explicit-any` due to this issue, but otherwise we try to use the `"all"` Pyrefly `preset` to configure all available kinds as errors.

Would you consider changing Pyrefly to handle this similar to Mypy which I understand only reports `explicit-any` for first-party types and annotations that explicitly use `Any` but does not attribute explicit `Any` from third-party aliases to first-party use sites?

## Minimal Pyrefly Reproduction

`pyrefly.toml`:

```toml
preset = "all"
python-version = "3.12"
python-platform = "linux"
```

`repro.py`:

```python
import numpy as np
import numpy.typing as npt

def repro(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
return x
```

Run:

```bash
uv run --isolated --no-project --python python3.12 \
--with pyrefly==1.1.1 \
--with numpy==2.5.1 \
pyrefly check \
--config pyrefly.toml \
--color=never \
--summary=none \
--progress-bar no \
repro.py
```

## Actual Output

```text
ERROR Explicit `Any` is not allowed [explicit-any]
--> repro.py:5:14
|
5 | def repro(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
| ^^^^^^^^^^^^^^^^^^^^^^^
|

ERROR Explicit `Any` is not allowed [explicit-any]
--> repro.py:5:42
|
5 | def repro(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
| ^^^^^^^^^^^^^^^^^^^^^^^
|
```

## Expected Behavior

I would expect Pyrefly not to report `explicit-any` at the first-party use site when the first-party source did not explicitly write `Any`.
In this case, the explicit `Any` is coming from NumPy's own type aliases or stubs.

If this behavior is intentional, it would be useful to have a targeted way to suppress `explicit-any` from configured third-party modules such as `numpy.typing`, without disabling `explicit-any` globally and without replacing those imports with `Any`.

## Comparison With Mypy

Mypy accepts the repro under strict mode plus explicit-Any checking:

`mypy.ini`:

```ini
[mypy]
strict = True
disallow_any_explicit = True
```

`repro.py`:

```python
import numpy as np
import numpy.typing as npt

def repro(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
return x
```

Run:

```shell
uv run --isolated --no-project --python python3.12 \
--with mypy \
--with numpy==2.5.1 \
mypy --config-file mypy.ini repro.py
```

```text
Success: no issues found in 1 source file
```

If `no_silence_site_packages = True` is enabled, Mypy reports many NumPy-internal errors, which suggests Mypy normally avoids projecting library-internal explicit `Any` into the user file.

Meanwhile if we do use explicit `Any` in first-party code, that gets flagged as an error as we desire:

`first_party_any_control.py`:

```python
from typing import Any

def repro(x: Any) -> Any:
return x
```

Run:

```shell
uv run --isolated --no-project --python python3.12 \
--with mypy \
--with numpy==2.5.1 \
mypy --config-file mypy.ini first_party_any_control.py
```

Outputs:

```text
first_party_any_control.py:4: error: Explicit "Any" is not allowed [explicit-any]
Found 1 error in 1 file (checked 1 source file)
```

## Workaround Tried

`replace-imports-with-any = ["numpy.typing"]` avoids the diagnostic, but it also discards useful NumPy typing precision.
Disabling `explicit-any` globally is too broad for projects that want to use it to block usages of explicit `Any` in first-party code.

## Environment

- Pyrefly: `1.1.1`
- Python: `3.12`
- NumPy: `2.5.1`
- OS: Linux

### Sandbox Link

Note the Pyrefly sandbox can't import third party code like numpy, so this example is a little contrived given the `numpy.py` we use is probably being checked by the sandbox as first-party code:

https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeS4ATrgLYAE6ArjWXRC7pQC50ByAIgEFKlVKQA0dMFFyouAZgBMAHXSrMMMHUoxi1ABT5EfISLEBtabIWKAugEo6AWgB8J4aNKWZcpbcSqdEHaMFyMlOh0%2BCDiIGQ60qSEXLRQFADEdAAKpAlQpHRoWHj4dADGuOiQAObhchCVhKqZAMowMHQAFlxcxHCIAPQD8Zr5hJzVAzDoA5i4ZXADFVUQtaJcDTNSnHSoAG6o0KjYsOWVNXUblXS4xFfocE3oZFydlU57MJRwm3QAvHRlCB5IQAIwqECqPTwUL-QEgVBQKBAmIgJgsJJkChgaj0LikYgQdDVNgcbh0QToCR0ADi0y%2BEDKkgAKgSYIIoBBUHAWWyAGqoSiqVQtTqoYgwZlw1kSgWUfRA0XiyVA%2ByqfgyyXS-mChUgDVs5mqkVlRGCqUAzVyvUtU1Qc3G9C2s2UZkAfQq2tlusVdvNHtwQMkFQOlC56C4f2ZlEYMDVanQZXtcDgdEw%2BIl%2Bjp6AZZXMzvtrocAUiwWI3LgwsTydTWEFniz9LDeaVEuZkgNbeLgTLFarSYrUh8NhLwTo5ZTVcyzNwIWInNNXA6rzkdAA7h05ugAOQ8DMwJxlTowMoAazorwglEw48F%2BLOGjonNPHXRZGa49yo1IxlW6E4MDmDA%2BDzoyEBcE4GCkLYqgCB4YjGJqHJcqmAJ1mYXiUtS6ZsvmfqugGti2EEIAAL6xKgZQbJ8ABi0AwBQRQ4AQJDkKRQA

### (Only applicable for extension issues) IDE Information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by running the command in the issue against repro.py with pyrefly.toml and confirm the explicit-any locations. Trace how Pyrefly attributes Any expanded from numpy.typing at those first-party annotations, then add or update coverage for the reproduction and verify that explicit Any written in first-party code still reports while the NDArray use sites do not.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.