Skip to content

fix Respect VERSIONS file in typeshed #2535#3093

Open
asukaminato0721 wants to merge 5 commits into
facebook:mainfrom
asukaminato0721:2535
Open

fix Respect VERSIONS file in typeshed #2535#3093
asukaminato0721 wants to merge 5 commits into
facebook:mainfrom
asukaminato0721:2535

Conversation

@asukaminato0721

@asukaminato0721 asukaminato0721 commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #2535

making bundled stdlib resolution respect typeshed’s stdlib/VERSIONS metadata.

bundled stdlib modules are now gated by the active python_version, so import distutils is rejected on Python 3.12+, and removed stdlib names are also filtered out of prefix results instead of leaking back in through third-party stubs.

Test Plan

add test

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 10, 2026 09:42
Copilot AI review requested due to automatic review settings April 10, 2026 09:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Pyrefly’s bundled typeshed integration to respect typeshed’s stdlib/VERSIONS metadata so that stdlib module availability is gated by the active python_version (e.g., distutils is rejected on Python 3.12+), and removed stdlib names no longer leak back into prefix completions via third-party stubs.

Changes:

  • Bundle typeshed/stdlib/VERSIONS and expose it via pyrefly_bundled::bundled_typeshed_versions().
  • Parse VERSIONS into version ranges and use it to gate stdlib module lookup and module-prefix enumeration by Python version.
  • Add tests covering version-gated import failures and prefix filtering.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pyrefly/lib/test/imports.rs Adds a regression test ensuring distutils import fails on Python 3.12.
pyrefly/lib/module/typeshed.rs Parses stdlib/VERSIONS and adds Python-version-aware stdlib module filtering APIs + unit test.
pyrefly/lib/module/finder.rs Gates bundled-stdlib resolution and prefix results by configured Python version; avoids third-party shadowing of removed stdlib.
crates/pyrefly_bundled/update.py Ensures stdlib/VERSIONS is included when trimming the upstream typeshed archive.
crates/pyrefly_bundled/third_party/typeshed/stdlib/VERSIONS Adds the typeshed stdlib version-availability metadata file to the repo.
crates/pyrefly_bundled/src/lib.rs Generalizes archive extraction and adds bundled_typeshed_versions() + test.
crates/pyrefly_bundled/build.rs Adds rerun-if-changed for stdlib/VERSIONS to keep the bundle up to date.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/module/typeshed.rs

@connernilsen connernilsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @asukaminato0721, thanks for adding this functionality! Just a few requests and then I think we should be good to import.

Comment thread pyrefly/lib/module/typeshed.rs
Comment thread crates/pyrefly_bundled/src/lib.rs Outdated
Comment thread pyrefly/lib/module/finder.rs
Comment thread pyrefly/lib/module/finder.rs
Comment thread pyrefly/lib/module/finder.rs
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@connernilsen

Copy link
Copy Markdown
Contributor

The changes look good, but seems like there's a failing test now. Can you fix? I'll pull in afterward

@connernilsen connernilsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for the broken test

@asukaminato0721

Copy link
Copy Markdown
Contributor Author
cargo test test::simple::test_tstring
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.14s
     Running unittests lib/lib.rs (target/debug/deps/pyrefly-fc4c0f0a68e04b9c)

running 1 test
test test::simple::test_tstring ... ok
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@connernilsen

Copy link
Copy Markdown
Contributor

Hmm, maybe it's some error on the base revision of your PR. I'll try pulling this in to see if I still see it.

@connernilsen connernilsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, two more things I caught in a last review

Comment thread pyrefly/lib/module/finder.rs Outdated
Comment thread crates/pyrefly_bundled/src/lib.rs Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added size/xl and removed size/xl labels May 16, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown

Diff from mypy_primer, showing the effect of this PR on open source code:

pip (https://github.com/pypa/pip)
- ERROR src/pip/_internal/locations/_distutils.py:92:22-76: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, str | None]) [no-matching-overload]
- ERROR src/pip/_internal/locations/_distutils.py:101:41-107:10: No matching overload found for function `posixpath.join` called with arguments: (str | Any | None, Literal['include'], Literal['site'], str, str) [no-matching-overload]

cloud-init (https://github.com/canonical/cloud-init)
+ ERROR cloudinit/distros/netbsd.py:15:12-17: Cannot find module `crypt` [missing-import]
+ ERROR cloudinit/sources/DataSourceAzure.py:157:20-25: Cannot find module `crypt` [missing-import]

jax (https://github.com/google/jax)
+ ERROR jax/_src/compilation_cache.py:27:3-31: Cannot find module `compression` [missing-import]

aioredis (https://github.com/aio-libs/aioredis)
+ ERROR aioredis/connection.py:11:1-44: Cannot find module `distutils.version` [missing-import]

setuptools (https://github.com/pypa/setuptools)
- ERROR setuptools/__init__.py:185:9-29: Class member `Command.reinitialize_command` overrides parent class `Command` in an inconsistent manner [bad-override]
- ERROR setuptools/_distutils/cmd.py:319:16-23: Returned type `distutils.cmd.Command | None` is not assignable to declared return type `setuptools._distutils.cmd.Command` [bad-return]
- ERROR setuptools/_distutils/cmd.py:334:54-83: No matching overload found for function `distutils.dist.Distribution.reinitialize_command` called with arguments: (Command | str, bool | Unknown) [no-matching-overload]
- ERROR setuptools/_distutils/command/build_ext.py:328:21-33: Argument `Literal[0, 1] | bool` is not assignable to parameter `verbose` with type `bool` in function `setuptools._distutils.compilers.C.base.new_compiler` [bad-argument-type]
- ERROR setuptools/_distutils/command/install_lib.py:152:25-37: Argument `Literal[0, 1] | bool` is not assignable to parameter `verbose` with type `bool` in function `setuptools._distutils.util.byte_compile` [bad-argument-type]
- ERROR setuptools/_distutils/command/sdist.py:389:38-61: No matching overload found for function `setuptools._distutils.filelist.FileList.exclude_pattern` called with arguments: (None, prefix=str) [no-matching-overload]
+ ERROR setuptools/_distutils/command/sdist.py:389:38-61: No matching overload found for function `setuptools._distutils.filelist.FileList.exclude_pattern` called with arguments: (None, prefix=Unknown) [no-matching-overload]
- ERROR setuptools/_distutils/compilers/C/base.py:1250:34-43: Argument `list[tuple[str, None, str]]` is not assignable to parameter `option_table` with type `list[tuple[str, str | None, str]] | None` in function `distutils.fancy_getopt.FancyGetopt.__init__` [bad-argument-type]
- ERROR setuptools/_distutils/dist.py:697:25-41: Argument `PathLike[str] | str | None` is not assignable to parameter `script_name` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `distutils.core.gen_usage` [bad-argument-type]
- ERROR setuptools/_distutils/dist.py:883:57-61: Argument `Self@setuptools._distutils.dist.Distribution` is not assignable to parameter `dist` with type `distutils.dist.Distribution` in function `setuptools._distutils.cmd.Command.__init__` [bad-argument-type]
- ERROR setuptools/_distutils/dist.py:972:44-56: Argument `Command | str` is not assignable to parameter `command` with type `str` in function `Distribution.get_command_obj` [bad-argument-type]
- ERROR setuptools/_distutils/dist.py:980:23-35: Cannot set item in `dict[str, bool]` [unsupported-operation]
- ERROR setuptools/command/__init__.py:16:9-37: Cannot set item in `list[str]` [unsupported-operation]
- ERROR setuptools/command/install_lib.py:94:9-18: Class member `install_lib.copy_tree` overrides parent class `install_lib` in an inconsistent manner [bad-override]
+ ERROR setuptools/config/pyprojecttoml.py:446:31-52: `Unknown | None` is not assignable to attribute `py_modules` with type `Never` [bad-assignment]
+ ERROR setuptools/config/pyprojecttoml.py:448:29-48: `Unknown | None` is not assignable to attribute `packages` with type `Never` [bad-assignment]

comtypes (https://github.com/enthought/comtypes)
+ ERROR comtypes/test/setup.py:3:1-33: Cannot find module `distutils.core` [missing-import]

apprise (https://github.com/caronc/apprise)
+ ERROR apprise/utils/pgp.py:48:16-22: Cannot find module `imghdr` [missing-import]
+ ERROR tests/test_utils_pgp.py:57:16-31: Cannot find module `imghdr` [missing-import]

streamlit (https://github.com/streamlit/streamlit)
- ERROR lib/tests/testutil.py:168:31-37: Could not import `Format` from `annotationlib` [missing-module-attribute]
- ERROR lib/tests/testutil.py:168:39-49: Could not import `ForwardRef` from `annotationlib` [missing-module-attribute]
+ ERROR lib/tests/testutil.py:168:5-49: Cannot find module `annotationlib` [missing-import]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ERROR ddtrace/vendor/psutil/setup.py:260:13-36: Object of class `UnixCCompiler` has no attribute `set_executable` [missing-attribute]
- ERROR ddtrace/vendor/psutil/setup.py:306:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:321:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:330:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `libraries` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:339:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:354:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:358:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:373:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:377:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:392:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:396:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:409:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `libraries` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:412:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:422:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:426:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:437:23-29: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `include_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `undef_macros` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `runtime_library_dirs` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_objects` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_compile_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `extra_link_args` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `export_symbols` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `swig_opts` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `depends` with type `list[str] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:440:9-25: Unpacked keyword argument `bool` is not assignable to parameter `language` with type `str | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:553:15-23: Argument `list[distutils.extension.Extension | setuptools.extension.Extension]` is not assignable to parameter `ext_modules` with type `list[distutils.extension.Extension]` in function `distutils.core.setup` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:553:15-23: Argument `list[distutils.extension.Extension | setuptools.extension.Extension]` is not assignable to parameter `ext_modules` with type `Sequence[setuptools._distutils.extension.Extension] | None` in function `setuptools.setup` [bad-argument-type]
- ERROR ddtrace/vendor/psutil/setup.py:641:27-33: Argument `list[tuple[str, int]]` is not assignable to parameter `define_macros` with type `list[tuple[str, str | None]] | None` in function `distutils.extension.Extension.__init__` [bad-argument-type]

mypy (https://github.com/python/mypy)
+ ERROR mypy/typeshed/stdlib/_zstd.pyi:3:1-74: Cannot find module `compression.zstd` [missing-import]
+ ERROR mypy/typeshed/stdlib/asynchat.pyi:1:8-16: Cannot find module `asyncore` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/__init__.pyi:4:1-54: Cannot find module `compression.zstd._zstdfile` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/__init__.pyi:7:8-13: Cannot find module `_zstd` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/__init__.pyi:8:1-102: Cannot find module `_zstd` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi:3:1-41: Cannot find module `compression._common` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi:4:1-38: Cannot find module `compression.zstd` [missing-import]
+ ERROR mypy/typeshed/stdlib/compression/zstd/_zstdfile.pyi:9:1-87: Cannot find module `_zstd` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/ccompiler.pyi:3:1-55: Cannot find module `distutils.file_util` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:5:1-52: Cannot find module `distutils.command.bdist_dumb` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:11:1-58: Cannot find module `distutils.command.build_scripts` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:12:1-42: Cannot find module `distutils.command.check` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:13:1-42: Cannot find module `distutils.command.clean` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:14:1-44: Cannot find module `distutils.command.config` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:17:1-64: Cannot find module `distutils.command.install_egg_info` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:18:1-62: Cannot find module `distutils.command.install_headers` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:21:1-48: Cannot find module `distutils.command.register` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:23:1-44: Cannot find module `distutils.command.upload` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/cmd.pyi:25:1-55: Cannot find module `distutils.file_util` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:5:1-52: Cannot find module `distutils.command.bdist_dumb` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:11:1-58: Cannot find module `distutils.command.build_scripts` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:12:1-42: Cannot find module `distutils.command.check` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:13:1-42: Cannot find module `distutils.command.clean` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:14:1-44: Cannot find module `distutils.command.config` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:17:1-64: Cannot find module `distutils.command.install_egg_info` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:18:1-62: Cannot find module `distutils.command.install_headers` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:21:1-48: Cannot find module `distutils.command.register` [missing-import]
+ ERROR mypy/typeshed/stdlib/distutils/dist.pyi:23:1-44: Cannot find module `distutils.command.upload` [missing-import]
- ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi:12:9-19: Class member `FixExitfunc.start_tree` overrides parent class `BaseFix` in an inconsistent manner [bad-override]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_exitfunc.pyi:2:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_itertools_imports.pyi:1:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_long.pyi:1:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_operator.pyi:1:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_reduce.pyi:1:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/lib2to3/fixes/fix_set_literal.pyi:1:1-31: Cannot find module `lib2to3` [missing-import]
+ ERROR mypy/typeshed/stdlib/smtpd.pyi:1:8-16: Cannot find module `asynchat` [missing-import]
+ ERROR mypy/typeshed/stdlib/smtpd.pyi:2:8-16: Cannot find module `asyncore` [missing-import]

discord.py (https://github.com/Rapptz/discord.py)
- ERROR discord/utils.py:1455:37-68: Object of class `ZstdDecompressor` has no attribute `decompressobj` [missing-attribute]

attrs (https://github.com/python-attrs/attrs)
+ ERROR src/attr/_compat.py:21:12-25: Cannot find module `annotationlib` [missing-import]
@github-actions

Copy link
Copy Markdown

Primer Diff Classification

✅ 12 improvement(s) | 12 project(s) total | +48, -38 errors

12 improvement(s) across pip, cloud-init, jax, aioredis, setuptools, comtypes, apprise, streamlit, dd-trace-py, mypy, discord.py, attrs.

Project Verdict Changes Error Kinds Root Cause
pip ✅ Improvement -2 no-matching-overload find_for_python_version()
cloud-init ✅ Improvement +2 missing-import for version-removed stdlib module (crypt) find_for_python_version()
jax ✅ Improvement +1 missing-import find_for_python_version()
aioredis ✅ Improvement +1 missing-import parse_versions()
setuptools ✅ Improvement +3, -13 bad-assignment with Never types find_for_python_version()
comtypes ✅ Improvement +1 missing-import parse_versions()
apprise ✅ Improvement +2 missing-import parse_versions()
streamlit ✅ Improvement +1, -2 missing-import, missing-module-attribute parse_versions()
dd-trace-py ✅ Improvement -19 bad-argument-type, missing-attribute parse_versions()
mypy ✅ Improvement +36, -1 missing-import on version-filtered stdlib modules find_for_python_version()
discord.py ✅ Improvement -1 missing-attribute find_for_python_version()
attrs ✅ Improvement +1 version-gated stdlib import find_for_python_version()
Detailed analysis

✅ Improvement (12)

pip (-2)

The two removed no-matching-overload errors in _distutils.py were false positives related to how pyrefly resolved the distutils module. The PR fixes pyrefly to respect typeshed's stdlib/VERSIONS file, which documents that distutils is only available in Python 3.0-3.11.

For error 1 (line 92): scheme.update({"purelib": i.install_lib, "platlib": i.install_lib})scheme is dict[str, str] and i.install_lib is typed as str | None in the distutils stubs, making the argument dict[str, str | None], which is incompatible with MutableMapping.update on a dict[str, str].

For error 2 (line 101): os.path.join(prefix, ...)prefix can be str | None (from the function parameter at line 39) or attributes from the distutils install command that may include None or Any types. os.path.join doesn't accept None.

Before this fix, when checking with Python 3.12+, pyrefly's module resolution for distutils was incorrect because it didn't account for the stdlib version constraints. This led to the distutils types being resolved in a way that produced these overload matching failures. With the fix properly respecting VERSIONS, the module resolution changes, and these errors are eliminated. Note that the source file includes the comment # mypy: strict-optional=False (line 4), indicating this code was written expecting a type checker mode where None is compatible with str — pyrefly doesn't support this directive, but the resolution change from the VERSIONS fix apparently resolves the type mismatches through a different mechanism. This is an improvement in pyrefly's module resolution accuracy.

Attribution: The changes in pyrefly/lib/module/typeshed.rs (adding find_for_python_version(), is_known_but_unavailable_for_python_version(), and the VersionRange parsing) and pyrefly/lib/module/finder.rs (the unavailable_stdlib_module check that prevents fallthrough to third-party stubs, and the use of find_for_python_version() instead of find()) directly caused these errors to disappear. Previously, when distutils was unavailable in the stdlib for the configured Python version, pyrefly would fall through to setuptools' third-party distutils stubs, which apparently had different type signatures causing the no-matching-overload errors. Now the fallthrough is blocked by the unavailable_stdlib_module guard in find_import_internal().

cloud-init (+2)

missing-import for version-removed stdlib module (crypt): The crypt module is unavailable in Python 3.13+ per typeshed's VERSIONS file. Both errors are co-reported by mypy and pyright. The PR correctly gates stdlib module resolution by Python version. These are true positives — improvement.

Overall: The crypt module was removed from the Python stdlib in Python 3.13 (deprecated in 3.11, removed in 3.13 per PEP 594). The typeshed VERSIONS file records crypt: 3.0-3.12. Both mypy and pyright also flag these imports. The PR correctly implements version-gated stdlib resolution, and these errors are true positives — the module genuinely doesn't exist for the configured Python version. While the cloud-init code handles this gracefully at runtime via try/except, the type checker is still correct to report that the module cannot be found.

Attribution: The PR changes in pyrefly/lib/module/typeshed.rs add version-range parsing from the stdlib/VERSIONS file and introduce find_for_python_version() which gates module resolution by Python version. The change in pyrefly/lib/module/finder.rs calls ts.find_for_python_version(module, config.[python_version()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/module/typeshed.rs)) instead of ts.find(module) for non-typeshed-internal imports. Since crypt: 3.0-3.12 in the VERSIONS file, any project configured for Python 3.13+ will now get missing-import for import crypt. The cloud-init project is presumably configured for Python 3.13+ (or the default version is 3.13+).

jax (+1)

The compression module was added to the Python stdlib in 3.14 (per typeshed's VERSIONS file: compression: 3.14-). The JAX code at line 27 does from compression import zstd inside a try/except block, which is valid runtime code but the import itself is unresolvable for Python < 3.14. Both mypy and pyright agree this is a missing import. The PR correctly implements version-gated stdlib resolution, and this error is a true positive — the module genuinely doesn't exist for the Python version being checked. The code handles this gracefully at runtime via the try/except, but from a type-checking perspective, the import is correctly flagged as missing.
Attribution: The PR changes in pyrefly/lib/module/typeshed.rs and pyrefly/lib/module/finder.rs implement stdlib/VERSIONS-based filtering. The VERSIONS file (added in the diff) shows compression: 3.14-, meaning the compression module is only available from Python 3.14 onwards. The find_for_python_version() method in typeshed.rs now filters out modules whose version range doesn't include the active Python version. Since JAX is likely being checked against Python < 3.14 (the default), the compression module is now correctly rejected. Previously, pyrefly would have found the compression stubs in typeshed without checking version availability.

aioredis (+1)

This is a correct new error. The distutils module was removed from Python's stdlib in 3.12 (PEP 632). The aioredis project imports from distutils.version import StrictVersion at line 11, which would fail at runtime on Python 3.12+ without setuptools installed. Pyrefly now correctly respects typeshed's stdlib/VERSIONS metadata, which records distutils: 3.0-3.11. Pyright also flags this same issue. The PR's test case test_removed_stdlib_module_respects_python_version explicitly tests that import distutils produces an error on Python 3.12. This is a genuine improvement in pyrefly's module resolution accuracy.
Attribution: The PR adds stdlib/VERSIONS file parsing in pyrefly/lib/module/typeshed.rs (the parse_versions() function and VersionRange struct), and gates module resolution through find_for_python_version() in the same file. The find_import_internal() function in pyrefly/lib/module/finder.rs was updated to call ts.find_for_python_version(module, config.[python_version()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/module/typeshed.rs)) instead of ts.find(module) for non-typeshed-origin imports. This means when the configured Python version is 3.12+, distutils (which has version range 3.0-3.11 in VERSIONS) is correctly rejected as unavailable.

setuptools (+3, -13)

bad-assignment with Never types: Two new errors where py_modules and packages attributes on Distribution are inferred as Never instead of their actual types. The Never type indicates a type inference failure — these are well-known attributes that should have types like list[str] | None. This is likely a regression from the new version-gating causing the Distribution class to be resolved differently, losing some type information. Pyright also flags these lines (2/2 cross-check), though we don't know the exact types pyright infers for these attributes.
no-matching-overload with Unknown: One new error where exclude_pattern(None, prefix=base_dir) fails because base_dir is Unknown. The value comes from self.distribution.get_fullname() which should return str. The Unknown type indicates an inference failure, likely because Distribution type information changed with the new module resolution. This is a regression.
Removed bad-argument-type errors: Six removed errors like Literal[0, 1] | bool not assignable to bool. These were false positives from dual distutils/setuptools type resolution creating conflicting type unions. Removing them is an improvement.
Removed no-matching-overload errors: Two removed errors involving Unknown types in overload matching. These were false positives from poor module resolution. Removing them is an improvement.
Removed unsupported-operation errors: Two removed errors about dict operations. These were false positives from incorrect type resolution. Removing them is an improvement.
Removed bad-override errors: Two removed errors about inconsistent overrides between distutils and setuptools Command classes. These were false positives caused by pyrefly seeing both stdlib distutils and setuptools's vendored distutils simultaneously. Removing them is an improvement.
Removed bad-return error: One removed error about distutils.cmd.Command | None vs setuptools._distutils.cmd.Command. This was a false positive from dual module resolution. Removing it is an improvement.

Overall: This is a mixed result. The PR correctly implements stdlib VERSIONS filtering, which fixes 13 false positives caused by dual distutils resolution. However, it introduces 3 new errors:

  1. Two bad-assignment errors with Never types (pyprojecttoml.py lines 446, 448): The error messages say Unknown | None is not assignable to attribute py_modules with type Never and similar for packages. The Never type indicates a type inference failure — these attributes on Distribution should have concrete types like list[str] | None, not Never. This is a regression likely caused by the new version-gating changing which Distribution class definition is resolved. Pyright also flags these lines (2/2 cross-check), though we don't know the exact types pyright infers.

  2. One no-matching-overload error (sdist.py line 389): exclude_pattern called with (None, prefix=Unknown). Looking at the code, base_dir = self.distribution.get_fullname() returns a string, so prefix=base_dir should be str, not Unknown. The Unknown type suggests the return type of get_fullname() isn't being resolved properly. This is pyrefly-only.

Net: 13 errors removed (mostly false positives from dual distutils resolution) vs 3 errors added (2 with Never inference failures, 1 with Unknown inference failure). The removals are genuine improvements. The additions are regressions (inference failures). Overall the net effect is positive (10 fewer false positives), making this an improvement on balance.

Attribution: The changes in pyrefly/lib/module/typeshed.rs (adding find_for_python_version(), modules_for_python_version(), is_available_for_python_version()) and pyrefly/lib/module/finder.rs (the unavailable_stdlib_module logic and version-gated find_for_python_version call) changed which distutils stubs are resolved. On Python 3.12+, stdlib distutils is now correctly rejected, so setuptools's vendored _distutils is used instead. This fixes the dual-resolution conflicts (removing 13 errors) but introduces 3 new errors where the vendored distutils types aren't fully resolved — the Never types in py_modules and packages attributes suggest that Distribution class attributes are being inferred as Never because some type information from the now-excluded stdlib distutils stubs was needed.

comtypes (+1)

This is a correct new error. The distutils module was removed from Python's stdlib in version 3.12 (as documented in typeshed's VERSIONS file: distutils: 3.0-3.11). The code from distutils.core import setup in comtypes/test/setup.py would fail at runtime on Python 3.12+. Both mypy and pyright agree this is an error. The PR intentionally implements typeshed VERSIONS file support to correctly gate stdlib module availability by Python version, which is standard behavior for type checkers.
Attribution: The PR adds stdlib/VERSIONS parsing in pyrefly/lib/module/typeshed.rs (the parse_versions() function and VersionRange struct), and gates module resolution on version availability via find_for_python_version() in the same file. The find_import_internal() function in pyrefly/lib/module/finder.rs now calls ts.find_for_python_version(module, config.[python_version()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/module/typeshed.rs)) instead of ts.find(module) for non-typeshed origins. The VERSIONS file specifies distutils: 3.0-3.11, so when pyrefly is configured for Python 3.12+, distutils is correctly rejected. The PR also prevents fallthrough to third-party stubs (setuptools provides distutils stubs) via the unavailable_stdlib_module check in find_import_internal().

apprise (+2)

The imghdr module was removed from the Python standard library in Python 3.13 per PEP 594. The typeshed VERSIONS file records imghdr: 3.0-3.12. Pyrefly now correctly respects this metadata and reports import imghdr as a missing module when the configured Python version is 3.13+. Both mypy and pyright agree with this diagnostic. The fact that the user code wraps the import in try/except ImportError is a runtime workaround — from a static type checking perspective, the module genuinely doesn't exist for the target Python version, and flagging it is correct behavior. This is the same behavior as mypy and pyright, and the PR explicitly intends to achieve this ("bundled stdlib modules are now gated by the active python_version").
Attribution: The PR adds stdlib/VERSIONS file parsing to pyrefly's bundled typeshed resolution (changes in pyrefly/lib/module/typeshed.rs adding parse_versions(), VersionRange, find_for_python_version(), is_available_for_python_version(), and changes in pyrefly/lib/module/finder.rs to call find_for_python_version() instead of find() for non-typeshed origins). The VERSIONS file contains imghdr: 3.0-3.12, so when the configured Python version is 3.13+, pyrefly now correctly rejects import imghdr. Previously pyrefly would resolve the import against the bundled typeshed stubs without checking version availability.

streamlit (+1, -2)

This is an improvement in error quality. The annotationlib module is only available in Python 3.14+ (per typeshed's VERSIONS file). Previously, pyrefly found the module in bundled typeshed regardless of Python version and reported two confusing missing-module-attribute errors. Now it correctly reports a single missing-import error because the module is unavailable for the configured Python version. The code at line 168 (from annotationlib import Format, ForwardRef) is indeed only valid on Python 3.14+, and pyright agrees. The error count went from 2 (less accurate) to 1 (more accurate), which is a net improvement in both precision and error quality.
Attribution: The PR adds stdlib/VERSIONS parsing in pyrefly/lib/module/typeshed.rs (the parse_versions() function and VersionRange struct) and gates module resolution via find_for_python_version() in pyrefly/lib/module/finder.rs. The VERSIONS file includes annotationlib: 3.14-, meaning it's only available from Python 3.14 onwards. When the project's configured Python version is < 3.14, pyrefly now correctly rejects the import of annotationlib as missing-import instead of finding the bundled typeshed stub and then failing on individual attributes.

dd-trace-py (-19)

All 19 removed errors were false positives. They arose because pyrefly was resolving distutils from bundled typeshed stdlib stubs even on Python 3.12+, where distutils is no longer part of the standard library. The macros list contains tuples like ("PSUTIL_POSIX", 1) (type tuple[str, int]), which don't match the typeshed stub's define_macros parameter type of list[tuple[str, str | None]] | None. The set_executable method exists at runtime on CCompiler (the parent class of UnixCCompiler) and is also present in typeshed's distutils stubs, but the missing-attribute error likely arose from incomplete or incorrect stub resolution when distutils was being resolved on Python 3.12+ where it shouldn't be available at all. These type mismatches are artifacts of resolving the wrong module stubs. The PR correctly implements version-aware stdlib resolution by parsing typeshed's stdlib/VERSIONS metadata, so distutils is no longer resolved on 3.12+, eliminating these spurious errors.
Attribution: The changes in pyrefly/lib/module/typeshed.rs (adding parse_versions(), VersionRange, find_for_python_version(), is_available_for_python_version()) and pyrefly/lib/module/finder.rs (the unavailable_stdlib_module check in find_import_internal() and version filtering in find_import_prefixes()) caused pyrefly to stop resolving distutils from bundled typeshed when the Python version is 3.12+. The VERSIONS file added in crates/pyrefly_bundled/third_party/typeshed/stdlib/VERSIONS contains distutils: 3.0-3.11, which gates the module. This eliminated the 18 bad-argument-type and 1 missing-attribute errors that were downstream consequences of incorrectly resolving distutils stubs.

mypy (+36, -1)

missing-import on version-filtered stdlib modules: 36 new false positive errors where pyrefly applies Python version filtering to mypy's internal copy of typeshed stubs. These are cross-references between stdlib stubs (e.g., _zstd.pyi importing compression.zstd, asynchat.pyi importing asyncore) that are valid within typeshed but filtered out because the target Python version doesn't include them. The PR's bypass for bundled_typeshed_origin doesn't cover mypy's own typeshed copy. All 36 are pyrefly-only — regression.
removed bad-override on lib2to3: The bad-override error on FixExitfunc.start_tree disappeared because lib2to3 (3.0-3.12) is now filtered out for the configured Python version. This was likely a real type issue that's now invisible. Minor regression as a side effect of the version filtering.

Overall: The 36 new missing-import errors are false positives caused by pyrefly applying Python version filtering to typeshed stub files that are part of the mypy project's own copy of typeshed (at mypy/typeshed/stdlib/). These are NOT end-user imports — they are typeshed's internal cross-references between stdlib stubs. The PR correctly added a bypass for bundled typeshed origins (bundled_typeshed_origin check in find_import_internal()), but mypy's copy of typeshed at mypy/typeshed/stdlib/ is not recognized as bundled typeshed — it's treated as regular project files. So version filtering is incorrectly applied to these internal typeshed imports.

For example, _zstd.pyi imports from compression.zstd — both are 3.14+ modules that exist in typeshed but are filtered out for the default Python version (likely 3.12 or 3.13). Similarly, asynchat.pyi imports asyncore — both removed after 3.11. These cross-references within typeshed are valid and should not be flagged.

The removed bad-override error on lib2to3/fixes/fix_exitfunc.pyi is a minor regression — it was likely a real type issue that's now invisible because the entire lib2to3 module is filtered out.

All 36 new errors are pyrefly-only (0/36 in mypy, 0/36 in pyright), confirming these are false positives. This is a regression — the version filtering feature is correct in principle but is being over-applied to typeshed stub files within the mypy project.

Attribution: The PR adds stdlib/VERSIONS-based filtering in pyrefly/lib/module/typeshed.rs (new find_for_python_version(), is_available_for_python_version(), modules_for_python_version() methods) and pyrefly/lib/module/finder.rs (changes to find_import_internal() and find_import_prefixes()). The key issue is in find_import_internal(): when bundled_typeshed_origin is true, it bypasses version filtering (ts.find(module) instead of ts.[find_for_python_version()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/module/typeshed.rs)). However, the mypy project's own .pyi files are NOT bundled typeshed origins — they are files in the mypy repo at mypy/typeshed/stdlib/. So when pyrefly checks these files, it applies version filtering to their imports of other stdlib modules. The modules like compression.zstd (3.14+), asynchat (3.0-3.11), asyncore (3.0-3.11), and lib2to3 (3.0-3.12) are filtered out based on whatever Python version pyrefly defaults to for the mypy project.

discord.py (-1)

This is a clear improvement. The PR makes pyrefly respect typeshed's stdlib/VERSIONS metadata, which gates stdlib modules by Python version. The compression.zstd module (containing ZstdDecompressor) is only available from Python 3.14+. Before this fix, pyrefly was incorrectly resolving the from compression.zstd import ZstdDecompressor import (line 95 in discord/utils.py) to the bundled typeshed stdlib stub even on Python versions < 3.14. The stdlib's ZstdDecompressor has a different API than the third-party zstandard package's ZstdDecompressor — specifically, it lacks the decompressobj() method. This caused a false missing-attribute error on line 1455. With the version-aware resolution, compression.zstd is no longer resolved from typeshed for Python < 3.14, eliminating the false positive.
Attribution: The changes in pyrefly/lib/module/typeshed.rs (adding find_for_python_version(), is_available_for_python_version(), and the VersionRange parsing) combined with the changes in pyrefly/lib/module/finder.rs (using find_for_python_version() instead of find() for non-typeshed origins) caused this fix. Before the PR, the bundled typeshed's compression.zstd stub (available only from 3.14+) was being resolved for all Python versions, causing the ZstdDecompressor from the stdlib stub to shadow the third-party zstandard package's class. The stdlib ZstdDecompressor doesn't have decompressobj(), hence the false positive. Now that version filtering is applied, compression.zstd is correctly excluded for Python < 3.14.

attrs (+1)

version-gated stdlib import: The annotationlib module only exists in Python 3.14+ per typeshed's VERSIONS file. Pyrefly now correctly respects this metadata and reports that the module cannot be found when checking against Python < 3.14. Because the version guard uses an intermediate variable (PY_3_14_PLUS) rather than a direct sys.version_info comparison in the if condition, the type checker cannot narrow away this branch for older Python versions. Pyright also flags this for the same reason. This is a correct improvement in pyrefly's module resolution.

Overall: The annotationlib module is new in Python 3.14 (per the VERSIONS file: annotationlib: 3.14-). The import on line 21 is inside an if PY_3_14_PLUS: block, where PY_3_14_PLUS is a variable assigned on line 17 as sys.version_info[:2] >= (3, 14). At runtime, this guard ensures the import only executes on Python 3.14+, so the code never fails at runtime.

However, type checkers like pyright and pyrefly can only narrow branches based on direct sys.version_info comparisons in the if condition itself. Since the condition uses the intermediate variable PY_3_14_PLUS rather than a direct sys.version_info >= (3, 14) check, the type checker cannot determine that this branch is unreachable for Python < 3.14. As a result, it analyzes the branch and finds that annotationlib doesn't exist in the stdlib for the configured Python version (< 3.14).

Pyrefly is now correctly respecting typeshed's VERSIONS metadata, which marks annotationlib as available only from 3.14 onwards. When the module is imported in a branch that the type checker cannot prove is unreachable, it correctly reports missing-import. Pyright exhibits the same behavior for this specific code pattern (indirect version guard). This is a correct improvement in pyrefly's module resolution.

Attribution: The PR adds stdlib/VERSIONS parsing to pyrefly's bundled typeshed resolution. The VERSIONS file contains annotationlib: 3.14-, meaning annotationlib is only available on Python 3.14+. The changes in pyrefly/lib/module/typeshed.rs (specifically find_for_python_version() and VersionRange::contains()) and pyrefly/lib/module/finder.rs (the find_import_internal() function now calls ts.[find_for_python_version()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/module/typeshed.rs) instead of ts.find()) cause pyrefly to reject imports of modules that aren't available for the configured Python version. Since the attrs project is presumably being checked with a Python version < 3.14, annotationlib is correctly identified as unavailable.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (12 LLM)

@meta-codesync

meta-codesync Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D106806791.

@yangdanny97

Copy link
Copy Markdown
Contributor

I'm doing a typeshed update right now, so I'll try to pull this in and get it over the finish line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants