Skip to content

Remove Pyright: stub generation can move to pyrefly stubgen, but needs pyrefly >= 1.2.0 #2391

Open
1 of 1 issue completed

Description

@schloerke

Background

Following #2368, Pyrefly is the primary type checker (make check-types → check-pyrefly) and pyrefly-baseline.json is down to {"errors": []}. Pyright, however, is still fully wired in:

Where What
pyrightconfig.json still present, typeCheckingMode: "strict"
pyproject.toml:144 pyright>=1.1.407 dev dependency
Makefile:59-67 pyright --createstub folium/uvicorn/seaborn
Makefile:75 pyright-typings, a prerequisite of both check-pyright and check-pyrefly
Makefile:93-95, Makefile:107 check-pyright / pyright targets
.github/workflows/pytest.yaml:70-87 dedicated Pyright CI job, listed as a required check at line 433
pyproject.toml:227-229 permissive-ignores = true, kept solely so Pyrefly honors # pyright: ignore
source tree 1463 # pyright: ignore comments (vs 127 # pyrefly: ignore), across 481 of 1189 .py files, plus ~40 file-level rule toggles and 4 # pyright: basic

There are two independent blockers to removing it. This issue tracks the first, which is now understood; the second is noted at the bottom.

Blocker 1: stub generation (investigated)

make pyright-typings shells out to pyright --createstub for folium, uvicorn, and seaborn, so Pyright can't be dropped until stub generation is replaced. (matplotlib stubs come from a git clone of microsoft/python-type-stubs and are unaffected.)

pyrefly stubgen is a viable replacement, but only on an unreleased version.

What was verified

Generated stubs for all three packages with each tool, swapped them into typings/, and ran both checkers:

typings/ generated by pyrefly check pyright
pyright --createstub (current) 0 errors 0 errors
pyrefly 1.1.1 stubgen 0 errors 10 errors
pyrefly 1.2.0.dev3 stubgen 0 errors 3 errors
pyrefly 1.2.0.dev3 stubgen --include-private 0 errors 0 errors

Two distinct stubgen bugs account for the failures.

1. __all__ is dropped from emitted stubs (7 of the 10 errors)

uvicorn/supervisors/__init__.pyi loses __all__ = ["Multiprocess", "ChangeReload"]. Per the typing spec, a name imported in a stub via from X import Y is not re-exported unless it appears in __all__ or is written from X import Y as Y, so shiny/_uvicorn.py:14 fails with "Multiprocess" is unknown import symbol.

This is systemic, not uvicorn-specific: 0 of the stubs generated by 1.1.1 contained __all__, against 17 from Pyright across the 18 source modules that define it.

Fixed upstream in meta/pyrefly#3924, closed 2026-06-26 (commit 9752acc). Pyrefly 1.1.1 shipped 2026-06-18, so the fix has never appeared in a stable release — it's only in 1.2.0.dev*. With 1.2.0.dev3, __all__ coverage matches or beats Pyright (seaborn: 13 in source, 12 from Pyright, 13 from stubgen).

2. Private symbols are dropped while public aliases still reference them (remaining 3 errors)

uvicorn/_types.py defines:

class _WebSocketReceiveEventBytes(TypedDict): ...
class _WebSocketReceiveEventText(TypedDict): ...
WebSocketReceiveEvent = _WebSocketReceiveEventBytes | _WebSocketReceiveEventText

stubgen emits the public alias but omits both underscore-prefixed classes, leaving dangling references that resolve to Unknown and trip Pyright strict's reportUnknownVariableType in shiny/_uvicorn.py and shiny/_main/_run.py.

Passing --include-private works around this. I don't see an upstream issue for it — worth filing separately.

Proposed change, once pyrefly 1.2.0 is stable

Bump to pyrefly>=1.2.0 and replace the three typings/* recipes in Makefile:59-67. Resolve package paths without hardcoding a Python version (verified working):

typings/folium:
	@echo "Creating folium stubs"
	pyrefly stubgen "$$(python -c "import importlib.util as u,os;print(os.path.dirname(u.find_spec('folium').origin))")" \
		-o typings/folium --include-private

Runtime is comparable — roughly 7-9s per package.

Note

pyrefly check reported 0 errors (207 suppressed) in every configuration above, including the two broken ones. Pyrefly does not enforce the stub re-export rule, so it cannot detect this class of stub degradation; Pyright was the only thing that caught either bug. That's an argument for keeping the Pyright CI job for a while even after stubgen lands, rather than removing it in the same PR.

Blocker 2: shinylive typings (not yet investigated)

.github/workflows/pytest.yaml:71-73 says the separate Pyright job stays "until shinylive's typings are validated under Pyrefly." That's independent of stub generation and is the blocker that actually gates removal today. It needs its own investigation.

Suggested sequencing

  • File the --include-private bug upstream. Update 9/1: Skipping for now
  • Wait for pyrefly 1.2.0 stable; migrate pyright-typings → pyrefly stubgen. Pyright is then no longer required to run Pyrefly, though its CI job can stay. (chore: use pyrefly for type stubs #2479)
  • Investigate and resolve the shinylive typings question.
  • Remove the Pyright job, pyrightconfig.json, and the dev dependency.
  • Convert # pyright: ignore → # pyrefly: ignore (preferring specific rule codes), then flip permissive-ignores = false — as already noted in pyproject.toml:228.
  • Update the docs that still name Pyright as the type checker: CLAUDE.md:23,100,128-129,175, .github/copilot-instructions.md:64,85,294,317, .claude/skills/port-from-bslib/SKILL.md:338.

Activity

  1. added
    choreA maintenance task that doesn't affect functionality
    on Jul 30, 2026
  2. ChidiebereNjoku commented on Sep 1, 2026

    @ChidiebereNjoku
    Contributor

    Hi @schloerke, Pyrefly 1.2.0 is now stable. I’d like to work on the scoped stub-generation migration: bump Pyrefly to 1.2.0, replace the folium/uvicorn/seaborn Pyright stub recipes with pyrefly stubgen --include-private, and update the related Makefile target while keeping the Pyright checker and CI job in place.

  3. schloerke commented on Sep 1, 2026

    @schloerke
    CollaboratorAuthor

    @ChidiebereNjoku Yes, please! Thank you

  4. ChidiebereNjoku commented on Sep 1, 2026

    @ChidiebereNjoku
    Contributor

    Thanks, @schloerke! I’ve opened PR #2478 with the scoped changes. It’s ready for review, and the workflows are currently awaiting maintainer approval.

  5. added
    Priority: LowLow-impact bug, docs polish, papercut, or unclear low-severity request.
    ai-triage:doneMarks an issue whose AI triage workflow is complete.
    on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: LowLow-impact bug, docs polish, papercut, or unclear low-severity request.ai-triage:doneMarks an issue whose AI triage workflow is complete.choreA maintenance task that doesn't affect functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions