Skip to content

Document the Python stub regeneration workflow and pin its toolchain - #1017

Merged
IvanaGyro merged 16 commits into
masterfrom
claude/pybind11-stubgen-stubs-ebny1c
Jul 11, 2026
Merged

Document the Python stub regeneration workflow and pin its toolchain#1017
IvanaGyro merged 16 commits into
masterfrom
claude/pybind11-stubgen-stubs-ebny1c

Conversation

@IvanaGyro

@IvanaGyro IvanaGyro commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

Documents how to regenerate the committed PEP 561 stubs (cytnx/cytnx/*.pyi) and pins/tightens the toolchain so that regeneration is reproducible. No committed stubs are changed here — only docs, packaging, and the build/generator plumbing.

Changes

  • CONTRIBUTING.md — new "Regenerating the Python type stubs" section: when regeneration is required (any pybind/*.cpp binding change), the two pinned tool versions that make it reproducible, the build → regenerate → validate (mypy.stubtest) steps.

  • Pin the stub-generation toolchain (pyproject.toml)pybind11-stubgen ==2.5.5 in the dev extra (pybind11 was already ==3.0.4 in [build-system].requires); a floating stubgen silently reformats the committed stubs on an unrelated regen. Added mypy to the dev extra, since the docs tell contributors to run python -m mypy.stubtest. Each pin carries a comment reminding you to regenerate the stubs — and update the CMake version string — when it is bumped.

  • CMake (CMakeLists.txt) — removed the pybind11 FetchContent fallback (which could fetch v3.0.1 when no pybind11 was found). It is now find_package(pybind11 3.0.0 REQUIRED). The pip path provisions the pinned 3.0.4 via PEP 517 build isolation; conda-forge/system builds just compile and ship the already-committed static stubs, so their pybind11 version cannot drift the .pyi. A TODO tracks bumping the requirement to 3.0.4 to match the pin once conda-forge ships it (it currently provides 3.0.3, which a 3.0.4 requirement would reject — this was caught by CI on the conda build).

  • tools/generate_stubs.pyfind_extension() now prefers the installed cytnx.cytnx (exactly what the editable install just built), falling back to scanning build/, so python tools/generate_stubs.py needs no --extension argument.

Testing

  • pip install --editable '.[dev]' builds cleanly against the relaxed CMake — pybind11 is resolved via build isolation with the FetchContent fallback removed.
  • python tools/generate_stubs.py (no arguments) discovers the installed extension and regenerates all 7 .pyi files with sane output (real bindings present).
  • Docs / build-config change only; the committed stubs are not modified in this PR.

🤖 Generated with Claude Code

IvanaGyro and others added 2 commits July 8, 2026 08:37
pybind11-stubgen renders cytnx/cytnx/*.pyi from the built extension, so
a floating version can silently reformat the committed stubs on an
unrelated regen (enum handling, overload ordering, etc. differ across
releases). pybind11 itself is already pinned exactly in
[build-system].requires for the same reason; pin pybind11-stubgen the
same way in the dev extra so both halves of the stub-generation
toolchain are fixed.

Co-Authored-By: Claude <noreply@anthropic.com>
cytnx/cytnx/*.pyi is generated from the built extension by
tools/generate_stubs.py, not written by hand, and nothing enforces
that a pybind/*.cpp binding change comes with a matching stub update.
Add a CONTRIBUTING.md section spelling out when regeneration is
required, the pinned pybind11/pybind11-stubgen versions that make it
reproducible, the build/install/regenerate steps, and how to validate
the result with mypy.stubtest.

Co-Authored-By: Claude <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request adds documentation to CONTRIBUTING.md on how to regenerate Python type stubs and pins pybind11-stubgen to version 2.5.5 in pyproject.toml. The review feedback suggests adding mypy to the dev dependencies in pyproject.toml since it is used in the stub verification instructions, clarifying the Python version requirement for stub generation, and adjusting the mypy.stubtest command to run from outside the repository root to avoid import shadowing issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread pyproject.toml
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a81f26e3e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CONTRIBUTING.md
IvanaGyro and others added 2 commits July 8, 2026 08:41
CONTRIBUTING.md's stub regeneration section instructs contributors to
run `python -m mypy.stubtest cytnx.cytnx` to validate the committed
stubs, but `pip install --editable '.[dev]'` did not install mypy,
so following those instructions in a clean environment failed with
"No module named mypy".

Co-Authored-By: Claude <noreply@anthropic.com>
"the lowest supported interpreter (Python 3.10+)" reads as "any
version >= 3.10", contradicting "lowest" in the same sentence. Name
the exact version to remove the ambiguity.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbf011d8f8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CONTRIBUTING.md Outdated
CMakeLists.txt's find_package(pybind11 3.0.0 QUIET) accepts any
installed pybind11 >= 3.0.0, and falls back to FetchContent-ing
v3.0.1 when none is found on the system. A direct `cmake --preset`
configure/build reads pybind11 through that path, bypassing
[build-system].requires entirely, so it can silently use a pybind11
other than the 3.0.4 this section pins for reproducible stub
annotations. Routing the build through `pip install --editable
'.[dev]'` instead goes through PEP 517 build isolation, which
resolves the pinned pybind11==3.0.4 from [build-system].requires; this
also folds in the previously separate pybind11-stubgen/mypy install
step.

Co-Authored-By: Claude <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 55.68%. Comparing base (b4c1584) to head (c2da734).
⚠️ Report is 64 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #1017       +/-   ##
===========================================
+ Coverage   32.81%   55.68%   +22.86%     
===========================================
  Files         230      230               
  Lines       33039    32960       -79     
  Branches    13769       71    -13698     
===========================================
+ Hits        10841    18353     +7512     
+ Misses      14904    14587      -317     
+ Partials     7294       20     -7274     
Flag Coverage Δ
cpp 55.59% <ø> (+23.13%) ⬆️
python 63.08% <ø> (+1.24%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
C++ backend 53.34% <ø> (+19.72%) ⬆️
Python bindings 70.35% <ø> (+45.73%) ⬆️
Python package 63.08% <ø> (+1.24%) ⬆️
see 172 files with indirect coverage changes

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b4c1584...c2da734. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

IvanaGyro and others added 3 commits July 8, 2026 09:09
… versions

The stub-regeneration section named the exact pinned versions
(pybind11 ==3.0.4, pybind11-stubgen ==2.5.5) inline, which duplicates
the pins in pyproject.toml and goes stale the moment either is bumped.
Point at pyproject.toml instead, and move the "regenerate the stubs
when you bump this" reminder into a comment beside each pin
(pybind11, pybind11-stubgen, and the requires-python floor, since the
stubs are generated with the lowest supported interpreter). The doc
now describes which knob lives where without restating the values.

Co-Authored-By: Claude <noreply@anthropic.com>
BUILD_PYTHON looked up pybind11 with `find_package(pybind11 3.0.0
QUIET)` and, when none was found, fetched `v3.0.1` via FetchContent.
Both paths can bind a pybind11 other than the `==3.0.4` pinned in
pyproject.toml's [build-system].requires. Since pybind11 controls the
type annotations baked into the extension that tools/generate_stubs.py
turns into the committed cytnx/cytnx/*.pyi stubs, that silently
regenerates the stubs from the wrong version. Replace the lookup with
`find_package(pybind11 3.0.4 REQUIRED)` so the build fails loudly
unless the pinned pybind11 (provisioned via build isolation on the
`pip install` path) is present. Update the CONTRIBUTING regeneration
note, which described the removed FetchContent fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
…ild-dir

The regeneration recipe forced `--config-settings=build-dir=build/python`
so the generator's `build/` auto-discovery would find the freshly built
extension. That override is unnecessary: scikit-build-core builds the
editable install in a temporary tree regardless (it does not honor the
CMake preset's binaryDir), so the pinned build-dir only mattered for
discovery. Drop it and instead point `tools/generate_stubs.py
--extension` at the extension the editable install just placed on the
import path, which is unambiguous and avoids the auto-discovery picking
up a stale build under `build/`.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dbd4d6179e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CMakeLists.txt Outdated
find_package(pybind11 3.0.4 REQUIRED) broke the conda build, which
provisions pybind11 3.0.3 from conda-forge as a host dependency:
CMake rejected 3.0.3 as incompatible with the requested 3.0.4. The
exact 3.0.4 pin only needs to hold on the `pip` path that regenerates
the committed cytnx/cytnx/*.pyi stubs (enforced by
[build-system].requires); every other build merely compiles the
extension and ships the already-committed static stubs, so its
pybind11 version does not affect stub content. Require pybind11 to be
present with a compatible floor (3.0.0) instead of a specific version,
which keeps the FetchContent fallback removed while accepting the
conda-forge pybind11.

Co-Authored-By: Claude <noreply@anthropic.com>
@IvanaGyro
IvanaGyro force-pushed the claude/pybind11-stubgen-stubs-ebny1c branch from 74a05a4 to 3cfcc21 Compare July 8, 2026 09:19
IvanaGyro and others added 2 commits July 8, 2026 09:30
find_extension() only scanned build/, so the documented regeneration
recipe had to pass --extension pointing at the editable install's
extension (the editable install builds in a temporary tree, not under
build/). Look up the installed cytnx.cytnx first via importlib and fall
back to the build/ scan, so `python tools/generate_stubs.py` with no
arguments uses exactly the extension the editable install just placed
on the import path. Simplify the CONTRIBUTING recipe to drop the
--extension incantation accordingly.

Co-Authored-By: Claude <noreply@anthropic.com>
find_package(pybind11 3.0.0 REQUIRED) is looser than the ==3.0.4 pin
in [build-system].requires because conda-forge currently ships pybind11
3.0.3, which a 3.0.4 requirement would reject. Record a TODO to bump
the CMake requirement to 3.0.4 once conda-forge catches up, and add a
note beside the pyproject pin so a future pybind11 bump also updates
the CMake version string.

Co-Authored-By: Claude <noreply@anthropic.com>
@IvanaGyro IvanaGyro changed the title Document the Python stub regeneration process in CONTRIBUTING.md Document the Python stub regeneration workflow and pin its toolchain Jul 8, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b2e8b2236

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/generate_stubs.py
@IvanaGyro
IvanaGyro force-pushed the claude/pybind11-stubgen-stubs-ebny1c branch 2 times, most recently from e0e8ff1 to 7189b52 Compare July 8, 2026 14:30
The BuildAndTest job's `cmake --build --target test_main` step may
trigger a CMake reconfigure, which re-evaluates
find_package(pybind11). Declare pybind11 in the `dev`
optional-dependencies so `pip install --editable '.[dev]'` installs it
into the environment, and point CMake at it with CMAKE_PREFIX_PATH in
the gtest-build step so the reconfigure resolves it. This also covers
local `cmake --preset` builds, which likewise need pybind11 outside
build isolation.

Co-Authored-By: Claude <noreply@anthropic.com>
@IvanaGyro
IvanaGyro force-pushed the claude/pybind11-stubgen-stubs-ebny1c branch from 7189b52 to 97b6b2d Compare July 8, 2026 14:49

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 97b6b2d337

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/generate_stubs.py
find_extension() preferred any importable installed cytnx.cytnx before
falling back to scanning build/. If the environment has an unrelated
cytnx installed (e.g. a released wheel) alongside a freshly built local
extension under build/, that unrelated install would win, silently
regenerating the committed stubs from the wrong API. Only accept the
installed extension when the cytnx package's __path__ includes this
checkout's cytnx/ source directory, which holds for an editable install
of this checkout (`pip install --editable .`) and rejects everything
else, falling through to the build/ scan instead.

Resolving cytnx.cytnx via importlib.util.find_spec imports the parent
cytnx package as a side effect, and cytnx/__init__.py imports
cytnx.cytnx before verifying it has real content (e.g. __version__), so
a checkout with no built extension can leave an empty cytnx.cytnx
namespace module cached in sys.modules even though the overall import
fails. Clear both cytnx and cytnx.cytnx from sys.modules after probing
so a later import of the staged build/ extension in main() is not
shadowed by that state.

Co-Authored-By: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b35aa7a82d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/generate_stubs.py Outdated
find_installed_extension() unconditionally popped cytnx and cytnx.cytnx
from sys.modules after probing, but left every cytnx.* continuation
module (Storage_conti, Tensor_conti, ...) cached. Resolving
cytnx.cytnx imports the parent cytnx package as a side effect, which
runs cytnx/__init__.py to completion whenever a real extension is
found (even one this function goes on to reject as unrelated to this
checkout), including its `from .Storage_conti import *`-style imports.
Those submodules' @add_method decorators patch whichever Storage/
Tensor/... classes were live at that moment; if main() later imports a
distinct copy of the correct extension, it would reuse the cached
submodules without rerunning the decorators, silently dropping the
patched methods from the regenerated stubs.

Clear every "cytnx"/"cytnx.*" sys.modules entry, but only when the
probed extension is rejected (or unresolvable). Accepting a candidate
is only reachable through the same mechanism (an editable-install
meta-path finder redirecting cytnx.cytnx to a real compiled extension)
that also makes any later import resolve to the identical objects, so
leaving that accepted state cached is correct and necessary: clearing
it would force Storage_conti et al. to rerun against the same
already-patched classes, which raises "method already exists" from
add_ovld_method's duplicate-registration guard.

Co-Authored-By: Claude <noreply@anthropic.com>
@IvanaGyro
IvanaGyro force-pushed the claude/pybind11-stubgen-stubs-ebny1c branch from e6069e9 to a911b4f Compare July 8, 2026 19:04

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a911b4ff30

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/generate_stubs.py
@IvanaGyro
IvanaGyro requested a review from yingjerkao July 9, 2026 02:18
@IvanaGyro
IvanaGyro requested a review from pcchen July 9, 2026 02:18
@pcchen

pcchen commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Posted by Claude Code on behalf of @pcchen

Reviewed the current head (a911b4f). Docs/build-plumbing only — no committed stubs or runtime behavior change, CI fully green across all 19 checks, and the earlier bot findings were all either fixed in named commits or rejected with documented reasoning. The regeneration recipe and the pin-bump checklist comments in pyproject.toml are exactly the kind of documentation that keeps this reproducible.

Two small things, neither blocking:

  1. CONTRIBUTING.md ends with a double newline (```\n\n at EOF). The pre-commit end-of-file-fixer enforces exactly one, so the next pre-commit run --all-files will rewrite this file as churn inside someone else's PR. Worth folding in if the branch gets touched again before merge.

  2. The sys.modules cleanup comment slightly overstates its guarantee (tools/generate_stubs.py:126-129, from a911b4f). Deleting the cytnx* entries resets the import system, but pybind11's type registrations live in per-process internals and survive it. In the rare path where the probe successfully imported a foreign cytnx (e.g. an installed wheel) and was then rejected, the staged import of a different .so can still fail with generic_type: type "..." is already registered!. That's a loud crash rather than wrong stubs — this scenario was unreachable before the probe existed, and a crash is the right failure mode — so no code change requested; just consider a one-line caveat in that comment so a future debugger of the crash isn't misled into looking at sys.modules.

pre-commit's end-of-file-fixer enforces exactly one trailing newline, so
the extra blank line would be rewritten as churn by the next
pre-commit run --all-files in an unrelated PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
find_installed_extension()'s cleanup only removes cytnx/cytnx.* entries
from sys.modules; it does not touch pybind11's per-process C++ type
registry. If the rejected probe imported a foreign cytnx.cytnx that
registered pybind11 types under the same qualified names, a later
staged import of a different .so can still raise "generic_type: type
... is already registered!" instead of producing wrong stubs, so a
future debugger of that crash isn't misled into looking only at
sys.modules.

Co-Authored-By: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Member Author

Thanks for the review. Both nits from cdda7ec's review body:

  1. EOF blank line — already fixed directly in cdda7ec; confirmed identical to what I was about to push independently.
  2. sys.modules cleanup comment caveat — added in c2da734: notes that the cleanup only resets the import system, not pybind11's per-process type registry, so a rejected-then-different-.so path can still fail loudly with generic_type: type ... is already registered! rather than producing wrong stubs.

Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2da7346d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/generate_stubs.py
@IvanaGyro
IvanaGyro merged commit 7c2996a into master Jul 11, 2026
17 checks passed
@IvanaGyro
IvanaGyro deleted the claude/pybind11-stubgen-stubs-ebny1c branch July 11, 2026 04:39
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.

3 participants