posit-dev / posit-dev/py-shiny

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

Open
#2,391 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

ai-triage:done chore Priority: Low
Dominant language
Python
Stars
1.8k
Forks
135
Avg merge
2d 18h
Merged PRs (30d)
21

Description

Background

Following #2368, Pyrefly is the primary type checker (make check-typescheck-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 facebook/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-typingspyrefly stubgen. Pyright is then no longer required to run Pyrefly, though its CI job can stay. (#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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Makefile:59-67 and pyproject.toml:144, then verify that stable pyrefly 1.2.0 is available. Run the existing pyright-typings and type-check targets while comparing stubs generated by pyrefly stubgen with the current results. Done means the three stub-generation recipes use pyrefly with package paths resolved from Python, while the documented Pyright CI sequencing remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
build-system, ci-cd, tooling
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.