Skip to content

Add griffe API checking to cuda_core - #2300

Merged
mdboom merged 1 commit into
NVIDIA:mainfrom
mdboom:griffe
Jul 29, 2026
Merged

Add griffe API checking to cuda_core#2300
mdboom merged 1 commit into
NVIDIA:mainfrom
mdboom:griffe

Conversation

@mdboom

@mdboom mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This adds a CI job to run griffe over cuda_core to detect API breakage.

It checks both against the merge base of the PR and against the latest cuda-core tag.

This PR also deliberately makes an API breakage (the same one found in #2280) so we can confirm it's working.

One side effect of this is that griffe /requires/ the top-level __init__.py to have an __all__ so it's explicit about what the public API is. This is a minor ongoing maintenance burden, but probably worth the effort.

@copy-pr-bot

copy-pr-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@mdboom

mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@github-actions github-actions Bot added CI/CD CI/CD infrastructure cuda.core Everything related to the cuda.core module labels Jul 2, 2026
@mdboom

mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@mdboom

mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author
image

@github-actions

This comment has been minimized.

Comment thread .github/workflows/ci.yml Outdated
# Findings fail this job (shows red in the UI), but it never blocks merging
# the PR: it's intentionally excluded from `checks:`'s `needs`, so it isn't
# part of the required aggregator status.
api-check-core:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Need to wire this job up in the check status job.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I think we should eventually. I was thinking we could make it a "soft fail" to start with while we learn about whether it produces any false positives.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, I'm not sure we ever want this to fail. There will be times in our release cycle where we are ok with breaking the API (once something has been deprecated for some time). We could maybe convince griffe to understand that someday, but in the meantime, having human-override judgement in the loop is probably good enough.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In that case, we could admin-merge maybe? Or is there a way to skip griffe on a per-line basis (like # noqa)?

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.

We could introduce a griffe-override label or similar to enable merging. Or alternatively [API-BREAK] in the PR title, to make it totally obvious. Or both, so we can easily deal with griffe false positives.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The label thing might work. I don't know if we want to rely on admin merge given not many of us can do that.

I don't think per-line would help because we want to say "just this once it's ok to break API, but not after this point".

There are two important scenarios I have in mind where this is necsesary:

  1. Right before a major release, we break all the APIs we already told our users (through deprecation) that we were are going to break

  2. Between releases it is perfectly ok to break APIs of things that have been added since the last release. I think it's pretty common that new APIs get introduced, merged and then "revised" before an actual release. This is why I implemented comparing to the previous released version in this PR -- we could see that "even though this API broke against main, it didn't break against the latest release (because it's on new APIs). That feature of this PR won't actually work until the next cuda-core release, unfortunately, since 1.0.1 didn't have .pyi files (and it has a ton of type annotation bugs that that work revealed).

del _patch_rlcompleter_for_cython_properties


__all__ = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Q: If this is a must to make griffe happy, should we just unify the style and always use __all__ in every submodule, private or public?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's just needed in the public ones. I think that's a reasonable convention.

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.

Do we need to add something about this to AGENTS.md?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

#2347 added test infrastructure to make sure __all__ is kept in sync. Personally, I always prefer that kind of thing to AGENTS.md when possible.

@leofang

leofang commented Jul 2, 2026

Copy link
Copy Markdown
Member

Very nice! It shows warnings right at the offending diff!

截圖 2026-07-02 下午5 57 03

@mdboom

mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Very nice! It shows warnings right at the offending diff!

Yep. Someday maybe we can get it to put it in the .pyx file, not the generated .pyi file, but this is more than good enough to catch breakages.

@mdboom

mdboom commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Note: The changes that this is highlighting against the cuda-core-v1.0.1 tag are coming from the fact that cuda-core-v1.0.1 predates having .pyi files. So that's not anything to be concerned about.

This PR should work as-is for checking for changes in a specific PR. For changes against the latest release, we will need to wait until the next release of cuda-core (which will have .pyi files) and then we can do that as well. (That's not really useful for review, but it's useful to have all the changes in one place so that /when/ we make a breaking release we know exactly what all of them are for release notes purposes).

@rwgk rwgk 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.

gpt-5.5 (with manual edits/additions)

Findings

  • Low: cuda_core/cuda/core/__init__.py adds DeviceResources to top-level __all__, but it is documented in cuda_core/docs/source/api_private.rst, not cuda_core/docs/source/api.rst. Since cuda_core/AGENTS.md defines public API by __all__, this promotes a returned helper into the explicit public surface. I would either remove it from top-level __all__ or move it to the main API page.

  • Low: .github/workflows/ci.yml runs api-check-core only when cuda_core/ changes. A follow-up that edits only .github/actions/griffe-api-check/action.yml will not exercise the action. Consider gating on core || shared, or a narrower action-specific change flag.

  • Low: .github/actions/griffe-api-check/action.yml invokes unpinned uvx griffe in both comparisons. That is fine for a prototype, but if this becomes a trusted signal, pin griffe or route it through the repo-managed environment.

Maintenance Risk

A maintenance risk from adding __all__ is source-of-truth drift. New APIs can be imported and documented but omitted from __all__, stale names can remain after removals (although import * would catch those), and returned/private helper types can accidentally become SemVer-public. The DeviceResources mismatch is a concrete example: it is imported at the top level and now listed in __all__, but the docs currently treat it as a private/returned helper.

That said, an explicit __all__ is probably the right convention if griffe is going to define and compare the supported public API from it. The burden is manageable as long as there is a consistency check that catches drift early.

A simple guard against stale names could be:

def test_cuda_core_all_exports_resolve():
    import cuda.core

    missing = [name for name in cuda.core.__all__ if not hasattr(cuda.core, name)]
    assert missing == []

Suggested Follow-On Idea

Add a follow-on docs/API consistency test that compares the public symbols in cuda.core.__all__ against the public entries in cuda_core/docs/source/api.rst, with deliberate exceptions for submodule namespaces and returned-helper/private API docs.

It should parse the Sphinx autosummary and data entries, then account for known surfaces such as cuda.core.graph, cuda.core.texture, cuda.core.utils, cuda.core.checkpoint, and entries intentionally documented in api_private.rst. The useful failure mode is: “this symbol is public by __all__ but missing from public docs” or “this symbol is documented as public but not exported by __all__.”

Comment thread .github/workflows/ci.yml Outdated
# Findings fail this job (shows red in the UI), but it never blocks merging
# the PR: it's intentionally excluded from `checks:`'s `needs`, so it isn't
# part of the required aggregator status.
api-check-core:

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.

We could introduce a griffe-override label or similar to enable merging. Or alternatively [API-BREAK] in the PR title, to make it totally obvious. Or both, so we can easily deal with griffe false positives.

@mdboom

mdboom commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@rwgk: Do you want to file an issue for keeping __all__ and the docs in sync? (That seems totally unrelated to this PR).

@leofang leofang added the PR review get-together Mark PRs you'd like the team to review at the weekly PR review get-together. label Jul 6, 2026
@mdboom

mdboom commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Heads up -- I am going to wait for the cuda.core 1.1.0 release to come back and finish this PR. Things will work much better when the latest tagged release as .pyi files. (1.0.1 predates shipping .pyi files).

@rwgk

rwgk commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@rwgk: Do you want to file an issue for keeping __all__ and the docs in sync?

Done: #2326

(That seems totally unrelated to this PR).

Yes, absolutely.

@mdboom

mdboom commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@mdboom
mdboom force-pushed the griffe branch 2 times, most recently from 3d7cdd4 to 197b48c Compare July 24, 2026 15:00
@mdboom

mdboom commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

1 similar comment
@mdboom

mdboom commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

mdboom added a commit that referenced this pull request Jul 29, 2026
…2347)

* test(core): add cuda.core.__all__ vs public docs consistency check

Closes #2326.

Parses docs/source/api.rst (autosummary entries and data directives while
cuda.core is the active module) and compares the flat public names against
cuda.core.__all__ in both directions. Dotted entries such as graph.Graph or
checkpoint.Process are submodule namespaces and are excluded. Symbols
documented in api_private.rst are accepted as documented so returned-helper
docs do not fail the check.

The tests skip when cuda.core.__all__ is not defined, so this lands
independently of #2300 and activates once #2300 merges. Also adds the
__all__-names-resolve guard suggested in the #2300 review.

Signed-off-by: Aryan <aryansputta@gmail.com>

* test(core): land cuda.core.__all__ and extend docs check to public subpackages

Addresses review feedback that the consistency check was too narrow:

- Define cuda.core.__all__ (flat public namespace) so the check runs instead
  of skipping, and add an aggregated __all__ to cuda.core.graph derived from
  its star-imported submodules.
- Auto-discover public subpackages from cuda.core.__path__ (graph, system,
  texture, utils, and any added later; the internal cuNN wheel shims are
  excluded) and assert each defines a fully resolvable __all__.
- Cross-check each documented subpackage's __all__ against api.rst, handling
  both the dotted (graph.Graph) and flat (currentmodule) doc conventions.
  system is documented in api_nvml.rst, so its doc cross-check is skipped.

* test(core): parse API docs with docutils

* Update content to pass current tests

* Add docutils dependency to pyproject.toml

* Simplify checks.  No longer make sure that everything documented is public.

* test(core): document intentional scope limits of api docs consistency check

* Reorganize __all__

* Fix doc reference

* Address findings in PR

* Fix tests and make __all__ construction consistent

* test(core): drop IPC types from _memory package contents expectation

_ipc.__all__ is now empty, so `from cuda.core._memory import *` no longer
binds IPCAllocationHandle or IPCBufferDescriptor. Update the expected list
in test_package_contents to match.

Signed-off-by: Aryan <aryansputta@gmail.com>

---------

Signed-off-by: Aryan <aryansputta@gmail.com>
Co-authored-by: Michael Droettboom <mdroettboom@nvidia.com>
Co-authored-by: Michael Droettboom <mdboom@gmail.com>
@mdboom

mdboom commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test

@mdboom
mdboom marked this pull request as ready for review July 29, 2026 12:20
@mdboom
mdboom requested review from leofang and rwgk July 29, 2026 12:22
@mdboom mdboom added this to the cuda.core next milestone Jul 29, 2026

@juenglin juenglin 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.

Left a question about tool version pinning.

PACKAGE_DIR: ${{ inputs.package-dir }}
MERGE_BASE: ${{ inputs.merge-base }}
run: |
uvx griffe check "$PACKAGE_NAME" \

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.

If this is use of a new third-pary tool, do we need to get it approved first?

Secondly, you are not pinning a version but we are pinning versions of other tools (like github actions). What's the thinking or general rule of thumb for when to pin?

@mdboom
mdboom enabled auto-merge (squash) July 29, 2026 22:47
@mdboom mdboom added the enhancement Any code-related improvements label Jul 29, 2026
@mdboom
mdboom merged commit 51d402c into NVIDIA:main Jul 29, 2026
108 of 110 checks passed
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

Copy link
Copy Markdown
Doc Preview CI
Preview removed because the pull request was closed or merged.

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

Labels

CI/CD CI/CD infrastructure cuda.core Everything related to the cuda.core module enhancement Any code-related improvements PR review get-together Mark PRs you'd like the team to review at the weekly PR review get-together.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants