Skip to content

Ship .pyi stubs in binary wheels#10

Open
Igralino wants to merge 1 commit into
neurobionics:mainfrom
Igralino:fix/ship-pyi-stubs-in-wheels
Open

Ship .pyi stubs in binary wheels#10
Igralino wants to merge 1 commit into
neurobionics:mainfrom
Igralino:fix/ship-pyi-stubs-in-wheels

Conversation

@Igralino

@Igralino Igralino commented Apr 17, 2026

Copy link
Copy Markdown

Problem

Was adding type hints to a project using pyopensim and hit this from Pylance on muscle_set: osim.simulation.SetMuscles:

Variable not allowed in type expression reportInvalidTypeForm

Hovering osim.simulation showed (variable) simulation: Any instead of a module. Code still ran fine.

Debug

Go-to-definition in VS Code jumped to __init__.py line 74:

try:
    from . import simulation
except ImportError as e:
    simulation = None

That = None assignment is what Pylance was picking up — which shouldn't happen if .pyi stubs exist, since they'd override .py. So I checked the installed wheel:

$ unzip -l pyopensim-4.5.2.0-cp311-cp311-win_amd64.whl | grep -c '\.pyi$'
0

Checked all three platform wheels on PyPI — same story. py.typed present, pyopensim-stubs/ directory missing entirely. Only the sdist ships stubs.

Root cause

The install rule reads stubs from ${CMAKE_CURRENT_BINARY_DIR}/stubs/pyopensim/, populated by scripts/python/generate_stubs.py. That script:

  1. Does sys.path.insert(0, str(package_path.parent)) — which doesn't propagate to the python -m mypy.stubgen subprocess it then spawns (no PYTHONPATH set either). So stubgen can't import pyopensim at build time.
  2. Uses subprocess.run(..., check=False) and then success_count += 1 in the else branch — warnings count as success.
  3. install(DIRECTORY ... FILES_MATCHING PATTERN "*.pyi") against an empty dir silently installs nothing.

CI stays green, wheel ships without stubs.

Fix

Install from src/pyopensim/ instead of the build dir. Committed stubs are already correct — just bypass the broken regeneration step so wheels actually ship them.

Fixing generate_stubs.py properly (env propagation, loud failures) is a separate concern.

The install rule copied stubs from `${CMAKE_CURRENT_BINARY_DIR}/stubs/pyopensim/`,
which is populated by `scripts/python/generate_stubs.py`. That script shells
out to `python -m mypy.stubgen -m pyopensim.<module>` as a subprocess, but
the `sys.path.insert(...)` used to make the package importable only affects
the parent process and never propagates to the subprocess. As a result
stubgen can't find `pyopensim` at build time and exits with a warning.
Combined with `check=False` and the "count warnings as success" logic, the
failure is silently swallowed, leaving an empty `${STUB_FILES_DIR}` and a
wheel that has `py.typed` but no `.pyi` files. All three 4.5.2.0 platform
wheels on PyPI are affected; only the sdist ships stubs.

Install the committed source stubs from `src/pyopensim/*.pyi` instead, so
wheels ship working type information regardless of whether the optional
regeneration step runs. Fixing `generate_stubs.py` itself (env propagation,
loud failures, import-dependency ordering) is out of scope for this change.
@Igralino

Copy link
Copy Markdown
Author

@senthurayyappan can you have a look please?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant