Skip to content

fix More expressive sys.platform gating #795#2579

Open
asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
asukaminato0721:795
Open

fix More expressive sys.platform gating #795#2579
asukaminato0721 wants to merge 2 commits intofacebook:mainfrom
asukaminato0721:795

Conversation

@asukaminato0721
Copy link
Contributor

@asukaminato0721 asukaminato0721 commented Feb 27, 2026

Summary

Fixes #795

completes the platform-guarded import behavior so modules guarded by assert sys.platform == ... are type-checked and their imports are resolved using the correct platform SysInfo.

Test Plan

introduces a regression test that a win32-guarded module can import winreg without errors.

@meta-cla meta-cla bot added the cla signed label Feb 27, 2026
@asukaminato0721 asukaminato0721 force-pushed the 795 branch 2 times, most recently from 7d7d640 to b849e5e Compare February 27, 2026 16:34
@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 27, 2026 16:39
Copilot AI review requested due to automatic review settings February 27, 2026 16:39
Copy link

Copilot AI left a comment

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 extends Pyrefly’s platform-guard handling so that modules with top-level sys.platform guards (e.g., assert sys.platform == "win32" or if sys.platform != "win32": raise ...) are type-checked using an overridden SysInfo, allowing their imports (e.g., winreg) to resolve under the correct platform.

Changes:

  • Add detection of module-level sys.platform guards and derive an effective per-module platform override.
  • Apply the overridden SysInfo during module analysis and when creating Handles for that module’s imports.
  • Add a regression test ensuring a win32-guarded module can import winreg without errors.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pyrefly/lib/test/sys_info.rs Adds a regression test for platform-guarded module imports (winreg under win32 guard).
pyrefly/lib/state/state.rs Introduces module SysInfo override plumbing and uses it for import Handle creation / analysis context.
pyrefly/lib/binding/bindings.rs Applies module-level platform guard override to the Bindings builder’s sys_info.
crates/pyrefly_python/src/sys_info.rs Adds SysInfo::with_platform and implements module_platform_guard extraction from module statements.
Comments suppressed due to low confidence (1)

pyrefly/lib/state/state.rs:1041

  • stdlib is fetched before computing the module’s sys_info override, and it’s still fetched based on module_data.handle.sys_info() rather than the overridden sys_info. For platform-guarded modules this can cause analysis to use a stdlib built for the wrong platform/version. Compute sys_info first, then fetch stdlib via get_stdlib_for_sys_info(sys_info) (and use that in Context).
            let stdlib = self.get_stdlib(&module_data.handle);
            let config = module_data.config.read();
            let sys_info_override = module_sys_info_override(
                module_data.handle.sys_info(),
                module_data.state.get_ast().as_deref(),
            );
            let sys_info = sys_info_override
                .as_ref()
                .unwrap_or(module_data.handle.sys_info());
            let ctx = Context {
                require,
                module: module_data.handle.module(),
                path: module_data.handle.path(),
                sys_info,
                memory: &self.memory_lookup(),
                uniques: &self.data.state.uniques,
                stdlib: &stdlib,
                lookup: &self.lookup(module_data.dupe()),

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

Comment on lines +1425 to +1432
pub fn get_stdlib_for_sys_info(&self, sys_info: &SysInfo) -> Arc<Stdlib> {
if self.data.stdlib.len() == 1 {
// Since we know our one must exist, we can shortcut
return self.data.stdlib.first().unwrap().1.dupe();
}

self.data.stdlib.get(handle.sys_info()).unwrap().dupe()
self.data.stdlib.get(sys_info).unwrap().dupe()
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

get_stdlib_for_sys_info returns the only cached stdlib whenever self.data.stdlib.len() == 1, even if the requested sys_info is different. With platform-guarded modules you can now create Handles with a different platform than the initial run, so this shortcut can silently return a stdlib for the wrong platform (or mask missing entries). Consider removing the shortcut or only using it when the sole key matches sys_info (and otherwise ensure the stdlib for sys_info is computed/available).

Copilot uses AI. Check for mistakes.
}

pub fn module_platform_guard(body: &[Stmt]) -> Option<PythonPlatform> {
body.iter().find_map(platform_guard_from_stmt)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

module_platform_guard scans the entire module body (iter().find_map(...)) and will treat any matching assert sys.platform == ... / if sys.platform != ...: raise anywhere in the file as a module-level guard. That can incorrectly override SysInfo for modules that merely perform a later runtime check. Consider restricting the scan to the leading top-level statements (e.g., after an optional module docstring and possibly import sys) so only true module-guard patterns trigger an override.

Suggested change
body.iter().find_map(platform_guard_from_stmt)
for stmt in body {
if let Some(platform) = platform_guard_from_stmt(stmt) {
return Some(platform);
}
match stmt {
// Allow leading trivial statements (e.g. module docstring and imports)
Stmt::Expr(_) | Stmt::Import(_) | Stmt::ImportFrom(_) => continue,
// Any other non-guard statement means there is no module-level platform guard.
_ => break,
}
}
None

Copilot uses AI. Check for mistakes.
@yangdanny97
Copy link
Contributor

yangdanny97 commented Mar 3, 2026

There are panics from this change, you can see it in the mypy primer CI job

Example

ERROR Thread panicked, shutting down: panicked at pyrefly/lib/state/state.rs:2270:17:
LookupAnswer::get failed to find key, builtins KeyExport(Name("WindowsError")) (concurrent changes?)

Added a cached effective_sys_info to module state so once a platform guard is discovered it persists even if the AST is cleared.
Updated import/look‑up and step context construction to use the cached effective sys_info, eliminating the mismatch that led to missing builtins.WindowsError and the LookupAnswer::get panic.
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

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

AutoSplit (https://github.com/Toufool/AutoSplit)
- ERROR src/capture_method/BitBltCaptureMethod.py:57:17-30: No attribute `windll` in module `ctypes` [missing-attribute]
+ ERROR src/capture_method/BitBltCaptureMethod.py:81:27-62: Argument `tuple[int, int, Literal[4]]` is not assignable to parameter `value` with type `Sequence[SupportsIndex] | SupportsIndex` in function `numpy.ndarray.shape` [bad-argument-type]
- ERROR src/capture_method/DesktopDuplicationCaptureMethod.py:8:21-29: Could not import `COMError` from `_ctypes` [missing-module-attribute]
+ ERROR src/capture_method/DesktopDuplicationCaptureMethod.py:59:45-63:10: `_YieldT_co` is not assignable to attribute `display` with type `Display` [bad-assignment]
+ ERROR src/capture_method/WindowsGraphicsCaptureMethod.py:158:23-78: Argument `tuple[Unknown, Unknown, Literal[4]]` is not assignable to parameter `value` with type `Sequence[SupportsIndex] | SupportsIndex` in function `numpy.ndarray.shape` [bad-argument-type]
+ ERROR src/d3d11.py:26:16-31:6: `tuple[tuple[Literal['Data1'], type[c_ulong]], tuple[Literal['Data2'], type[c_ushort]], tuple[Literal['Data3'], type[c_ushort]], tuple[Literal['Data4'], type[Array[c_ubyte]]]]` is not assignable to attribute `_fields_` with type `Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]` [bad-assignment]
- ERROR src/d3d11.py:35:22-40: No attribute `WINFUNCTYPE` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:41:14-32: No attribute `WINFUNCTYPE` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:42:15-33: No attribute `WINFUNCTYPE` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:53:19-34: No attribute `WinError` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:194:15-30: No attribute `WinError` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:199:21-39: No attribute `WINFUNCTYPE` in module `ctypes` [missing-attribute]
- ERROR src/d3d11.py:213:27-40: No attribute `windll` in module `ctypes` [missing-attribute]

PyWinCtl (https://github.com/Kalmat/PyWinCtl)
+ ERROR src/pywinctl/_pywinctl_macos.py:62:28-107: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:91:28-107: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:309:24-41: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:351:23-38: Type `enumerate[@_]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:356:29-46: Type `enumerate[@_]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:391:20-33: Type `_YieldT_co` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:434:28-435:92: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:457:23-40: Type `enumerate[@_]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:523:32-524:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:617:32-618:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:643:32-644:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:684:36-685:100: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:715:40-716:104: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:728:40-729:104: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:741:36-742:100: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:773:32-774:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:808:32-809:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:847:32-848:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:991:32-992:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1017:32-1018:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1073:32-1074:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1119:32-1120:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1218:32-1219:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1258:32-1259:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1306:50-80: No matching overload found for function `difflib.get_close_matches` called with arguments: (str, list[Any] | Any, n=Literal[1]) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1347:32-1348:96: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1463:48-1464:112: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1499:32-44: No matching overload found for function `list.__init__` called with arguments: (Sequence[int] | list[@_]) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1506:36-58: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1509:55-119: `list[@_] | Unknown` is not assignable to `Sequence[tuple[str, str, bool, str]] | str` [bad-assignment]
+ ERROR src/pywinctl/_pywinctl_macos.py:1533:40-56: `>` is not supported between `int` and `_T` [unsupported-operation]
+ ERROR src/pywinctl/_pywinctl_macos.py:1543:73-80: Argument `list[Unknown] | Unknown` is not assignable to parameter `subAttrList` with type `Sequence[Sequence[tuple[str, str, bool, str]]]` in function `subfillit` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1545:90-121: Argument `list[int | _T]` is not assignable to parameter `path` with type `Sequence[int] | None` in function `subfillit` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1550:32-73: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1553:87-127: Argument `list[@_] | Unknown` is not assignable to parameter `subAttrList` with type `Sequence[Sequence[tuple[str, str, bool, str]]]` in function `subfillit` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1554:87-96: Argument `list[int | Unknown]` is not assignable to parameter `path` with type `Sequence[int] | None` in function `subfillit` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1577:32-57: `list[Unknown] | list[str]` is not assignable to variable `itemPath` with type `Sequence[str] | None` [bad-assignment]
+ ERROR src/pywinctl/_pywinctl_macos.py:1582:36-61: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1603:44-1604:108: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1643:36-60: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1643:46-59: Argument `list[str] | list[_T]` is not assignable to parameter `iterable` with type `Iterable[str]` in function `enumerate.__new__` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1666:44-1667:108: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1704:38-62: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1704:48-61: Argument `list[str] | list[_T]` is not assignable to parameter `iterable` with type `Iterable[str]` in function `enumerate.__new__` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1727:44-1728:108: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1749:36-61: Type `enumerate[str]` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_macos.py:1749:46-60: Argument `list[str] | list[_T]` is not assignable to parameter `iterable` with type `Iterable[str]` in function `enumerate.__new__` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_macos.py:1772:44-1773:108: No matching overload found for function `subprocess.Popen.__init__` called with arguments: (list[str], stdin=int, stdout=int, encoding=Literal['utf8']) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1842:33-51: No matching overload found for function `dict.get` called with arguments: (str, dict[@_, @_]) [no-matching-overload]
+ ERROR src/pywinctl/_pywinctl_macos.py:1910:20-40: `+` is not supported between `_T` and `str` [unsupported-operation]
- ERROR src/pywinctl/_pywinctl_win.py:314:16-29: No attribute `windll` in module `ctypes` [missing-attribute]
- ERROR src/pywinctl/_pywinctl_win.py:366:9-22: No attribute `windll` in module `ctypes` [missing-attribute]
- ERROR src/pywinctl/_pywinctl_win.py:371:9-22: No attribute `windll` in module `ctypes` [missing-attribute]
- ERROR src/pywinctl/_pywinctl_win.py:461:9-22: No attribute `windll` in module `ctypes` [missing-attribute]
- ERROR src/pywinctl/_pywinctl_win.py:521:27-40: No attribute `windll` in module `ctypes` [missing-attribute]
- ERROR src/pywinctl/_pywinctl_win.py:522:27-40: No attribute `windll` in module `ctypes` [missing-attribute]
+ ERROR src/pywinctl/_pywinctl_win.py:82:12-77: Returned type `list[_T]` is not assignable to declared return type `list[Win32Window]` [bad-return]
+ ERROR src/pywinctl/_pywinctl_win.py:94:32-51: Argument `Win32Window` is not assignable to parameter `object` with type `_T` in function `list.append` [bad-argument-type]
+ ERROR src/pywinctl/_pywinctl_win.py:296:20-33: Type `_YieldT_co` is not iterable [not-iterable]
+ ERROR src/pywinctl/_pywinctl_win.py:353:20-357:10: `list[tuple[str, type[RECT]] | tuple[str, type[c_ulong]] | tuple[str, type[Array[c_ulong]]]]` is not assignable to attribute `_fields_` with type `Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]` [bad-assignment]
+ ERROR src/pywinctl/_pywinctl_win.py:439:16-450:6: `list[tuple[str, type[RECT]] | tuple[str, type[c_uint]] | tuple[str, type[c_ulong]] | tuple[str, type[c_ushort]]]` is not assignable to attribute `_fields_` with type `Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]` [bad-assignment]
+ ERROR src/pywinctl/_pywinctl_win.py:1183:44-71: `_VT` is not subscriptable [unsupported-operation]
+ ERROR src/pywinctl/_pywinctl_win.py:1183:51-63: Cannot index into `dict[Unknown, Unknown]` [bad-index]

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Primer Diff Classification

❌ 2 regression(s) | 2 project(s) total

2 regression(s) across AutoSplit, PyWinCtl. error kinds: Platform import resolution, Numpy shape assignment, Generator type inference. caused by module_platform_guard().

Project Verdict Changes Error Kinds Root Cause
AutoSplit ❌ Regression +4, -9 Platform import resolution module_platform_guard()
PyWinCtl ❌ Regression +57, -6 bad-argument-type, bad-assignment module_platform_guard()
Detailed analysis

❌ Regression (2)

AutoSplit (+4, -9)

Platform import resolution: IMPROVEMENT - Fixed false positives where pyrefly incorrectly claimed Windows-specific imports like ctypes.windll and _ctypes.COMError don't exist
Numpy shape assignment: REGRESSION - New false positive claiming tuple assignment to numpy array shape is invalid when it's standard usage
Generator type inference: REGRESSION - New false positive from failing to infer concrete type from generator expression, showing generic _YieldT_co instead

Overall: This is a mixed change with both improvements and regressions. The REMOVED errors were false positives - pyrefly was incorrectly claiming that ctypes.windll (line 57) and _ctypes.COMError (line 8) don't exist, when these are standard Windows-specific attributes/imports that exist when sys.platform == "win32". The PR fixed platform-guarded import resolution, so pyrefly now correctly recognizes these Windows-only imports.

However, the NEW errors appear to be regressions:

  1. bad-argument-type on numpy shape assignment (lines 81, 158): The errors claim tuple[int, int, Literal[4]] is not assignable to Sequence[SupportsIndex] | SupportsIndex. But numpy arrays accept tuple assignments to the shape attribute - this is standard numpy usage. The tuple (height, width, BGRA_CHANNEL_COUNT) where BGRA_CHANNEL_COUNT = 4 is perfectly valid.

  2. bad-assignment with generator expression (lines 59-63): The error claims _YieldT_co is not assignable to display with type Display. This suggests pyrefly is failing to infer the concrete type from the generator expression next(display for display in self._desktop_duplication.displays if display.hmonitor == hmonitor) and instead inferring a generic type variable.

The improvements (fixing false positive import errors) are valuable, but the new type inference failures create noise on valid code patterns.

Attribution: The changes to module_platform_guard() and related functions in crates/pyrefly_python/src/sys_info.rs and pyrefly/lib/state/state.rs improved platform-specific import resolution by detecting platform guards like if sys.platform != "win32": raise OSError and using the correct platform context for type checking.

PyWinCtl (+57, -6)

This is a regression. The PR improved platform-specific import handling but introduced type inference failures. Multiple error patterns indicate inference problems: 1) Never/@_ types appearing in error messages (7 out of 57 errors) - these are classic signs of type resolution failure, not real bugs. 2) no-matching-overload errors on valid subprocess.Popen calls with correct argument types. 3) not-iterable errors claiming enumerate[str] is not iterable, when it clearly is. 4) The removed error was a false positive about missing ctypes.windll attribute. While the platform guard detection improvement is good, the type inference degradation creates false positive noise that mypy/pyright would not produce.
Attribution: The changes to module_platform_guard() and related functions in crates/pyrefly_python/src/sys_info.rs and pyrefly/lib/binding/bindings.rs improved platform-specific import resolution. However, this also introduced type inference failures that manifest as Never/@_ types in error messages.

Suggested Fix

Summary: Platform guard detection improvements fixed import resolution but introduced type inference failures, causing Never types and generator inference regressions.

1. In effective_sys_info() in pyrefly/lib/state/state.rs, add a fallback mechanism: when module_sys_info_override() returns a platform override, validate that the override doesn't break existing type inference by checking if the AST contains generator expressions or complex type patterns. If so, use the original sys_info for type inference while keeping the platform override only for import resolution.

Files: pyrefly/lib/state/state.rs
Confidence: medium
Affected projects: AutoSplit, PyWinCtl
Fixes: Generator type inference, Numpy shape assignment
The effective_sys_info() method is where platform overrides are applied, but this is affecting type inference contexts beyond just imports. A selective application would preserve the import resolution improvements while avoiding type inference regressions. Expected outcome: eliminates 61 type inference errors (Never types, generator inference failures) across AutoSplit and PyWinCtl while preserving the 15 import resolution improvements.

2. In the Context creation in Transaction::get_context() in pyrefly/lib/state/state.rs, add a guard condition: use module_data.effective_sys_info() only for import resolution contexts, but fall back to module_data.handle.sys_info() for type inference contexts. This can be determined by checking if the context is being used for binding resolution vs type checking.

Files: pyrefly/lib/state/state.rs
Confidence: high
Affected projects: AutoSplit, PyWinCtl
Fixes: Generator type inference, Numpy shape assignment
The Context struct is used for both import resolution and type inference. The platform override should only affect import resolution, not type inference. By conditionally applying the override based on context usage, we can preserve both the import fixes and avoid type inference regressions. Expected outcome: eliminates all 61 type inference errors while keeping the 15 import resolution improvements.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (2 LLM)

@yangdanny97
Copy link
Contributor

The primer delta looks unfavorable, so I think this needs some work still. LMK if you want me to help dig into it

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

More expressive sys.platform gating

3 participants