Add griffe API checking to cuda_core - #2300
Conversation
|
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. |
|
/ok to test |
|
/ok to test |
This comment has been minimized.
This comment has been minimized.
| # 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: |
There was a problem hiding this comment.
Need to wire this job up in the check status job.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
In that case, we could admin-merge maybe? Or is there a way to skip griffe on a per-line basis (like # noqa)?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
-
Right before a major release, we break all the APIs we already told our users (through deprecation) that we were are going to break
-
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
.pyifiles (and it has a ton of type annotation bugs that that work revealed).
| del _patch_rlcompleter_for_cython_properties | ||
|
|
||
|
|
||
| __all__ = [ |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
It's just needed in the public ones. I think that's a reasonable convention.
There was a problem hiding this comment.
Do we need to add something about this to AGENTS.md?
There was a problem hiding this comment.
#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.
Yep. Someday maybe we can get it to put it in the |
|
Note: The changes that this is highlighting against the 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 |
rwgk
left a comment
There was a problem hiding this comment.
gpt-5.5 (with manual edits/additions)
Findings
-
Low:
cuda_core/cuda/core/__init__.pyaddsDeviceResourcesto top-level__all__, but it is documented incuda_core/docs/source/api_private.rst, notcuda_core/docs/source/api.rst. Sincecuda_core/AGENTS.mddefines 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.ymlrunsapi-check-coreonly whencuda_core/changes. A follow-up that edits only.github/actions/griffe-api-check/action.ymlwill not exercise the action. Consider gating oncore || shared, or a narrower action-specific change flag. -
Low:
.github/actions/griffe-api-check/action.ymlinvokes unpinneduvx griffein 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__.”
| # 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: |
There was a problem hiding this comment.
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.
|
@rwgk: Do you want to file an issue for keeping |
|
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 |
|
/ok to test |
3d7cdd4 to
197b48c
Compare
|
/ok to test |
1 similar comment
|
/ok to test |
…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>
|
/ok to test |
juenglin
left a comment
There was a problem hiding this comment.
Left a question about tool version pinning.
| PACKAGE_DIR: ${{ inputs.package-dir }} | ||
| MERGE_BASE: ${{ inputs.merge-base }} | ||
| run: | | ||
| uvx griffe check "$PACKAGE_NAME" \ |
There was a problem hiding this comment.
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?
This comment has been minimized.
This comment has been minimized.
1 similar comment
|


This adds a CI job to run
griffeovercuda_coreto detect API breakage.It checks both against the merge base of the PR and against the latest
cuda-coretag.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__.pyto 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.