-
Notifications
You must be signed in to change notification settings - Fork 266
cuda_core: derive error enum explanations from bindings docstrings #1860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
4cbe3f3
cuda_core: derive error enum explanations from bindings docstrings
rwgk 8b0c526
cuda_core: recognize 12.9.6 enum docstrings
rwgk 4fde3d6
cuda_core: drop unused enum doc cleanup helper
rwgk 7447964
cuda_core: clarify enum explanation helper docs
rwgk cddb565
cuda_core: trim enum explanation helper tests
rwgk 849b1b9
Merge branch 'main' into enum_explanations_from_docstrings
rwgk f70c099
cuda_core: generalize enum doc cleanup regexes
rwgk c4b5862
cuda_core: document inline enum cleanup regexes
rwgk 7025854
cuda_core: lazily import frozen enum explanation tables
rwgk 27aae00
cuda_core: pin cleanup-sensitive enum doc examples
rwgk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
366 changes: 11 additions & 355 deletions
366
cuda_core/cuda/core/_utils/driver_cu_result_explanations.py
Large diffs are not rendered by default.
Oops, something went wrong.
350 changes: 350 additions & 0 deletions
350
cuda_core/cuda/core/_utils/driver_cu_result_explanations_frozen.py
Large diffs are not rendered by default.
Oops, something went wrong.
130 changes: 130 additions & 0 deletions
130
cuda_core/cuda/core/_utils/enum_explanations_helpers.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE | ||
|
|
||
| """Internal support for error-enum explanations. | ||
|
|
||
| ``cuda_core`` keeps frozen 13.1.1 fallback tables for older ``cuda-bindings`` | ||
| releases. Driver/runtime error enums carry usable ``__doc__`` text starting in | ||
| the 12.x backport line at ``cuda-bindings`` 12.9.6, and in the mainline 13.x | ||
| series at ``cuda-bindings`` 13.2.0. This module decides which source to use | ||
| and normalizes generated docstrings so user-facing ``CUDAError`` messages stay | ||
| presentable. | ||
|
|
||
| The cleanup rules here were derived while validating generated enum docstrings | ||
| in PR #1805. Keep them narrow and remove them when codegen quirks or fallback | ||
| support are no longer needed. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import importlib.metadata | ||
| import re | ||
| from collections.abc import Callable | ||
| from typing import Any | ||
|
|
||
| _MIN_12X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (12, 9, 6) | ||
| _MIN_13X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (13, 2, 0) | ||
| _RST_INLINE_ROLE_RE = re.compile(r":(?:[a-z]+:)?[a-z]+:`([^`]+)`") | ||
| _WORDWRAP_HYPHEN_AFTER_RE = re.compile(r"(?<=[0-9A-Za-z_])- (?=[0-9A-Za-z_])") | ||
| _WORDWRAP_HYPHEN_BEFORE_RE = re.compile(r"(?<=[0-9A-Za-z_]) -(?=[0-9A-Za-z_])") | ||
| _ExplanationTable = dict[int, str | tuple[str, ...]] | ||
| _ExplanationTableLoader = Callable[[], _ExplanationTable] | ||
|
|
||
|
|
||
| # ``version.pyx`` cannot be reused here (circular import via ``cuda_utils``). | ||
| def _binding_version() -> tuple[int, int, int]: | ||
| """Return the installed ``cuda-bindings`` version, or a conservative old value.""" | ||
| try: | ||
| parts = importlib.metadata.version("cuda-bindings").split(".")[:3] | ||
| except importlib.metadata.PackageNotFoundError: | ||
| return (0, 0, 0) # For very old versions of cuda-python | ||
| return tuple(int(v) for v in parts) | ||
|
|
||
|
|
||
| def _binding_version_has_usable_enum_docstrings(version: tuple[int, int, int]) -> bool: | ||
| """Whether released bindings are known to carry usable error-enum ``__doc__`` text.""" | ||
| return ( | ||
| _MIN_12X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS <= version < (13, 0, 0) | ||
| or version >= _MIN_13X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS | ||
| ) | ||
|
|
||
|
|
||
| def _fix_hyphenation_wordwrap_spacing(s: str) -> str: | ||
| """Remove spaces around hyphens introduced by line wrapping in generated ``__doc__`` text. | ||
|
|
||
| This targets asymmetric wrap artifacts such as ``non- linear`` or | ||
| ``GPU- Direct`` while leaving intentional ``a - b`` separators alone. | ||
| """ | ||
| prev = None | ||
| while prev != s: | ||
| prev = s | ||
| s = _WORDWRAP_HYPHEN_AFTER_RE.sub("-", s) | ||
| s = _WORDWRAP_HYPHEN_BEFORE_RE.sub("-", s) | ||
| return s | ||
|
|
||
|
|
||
| def clean_enum_member_docstring(doc: str | None) -> str | None: | ||
| """Turn an enum member ``__doc__`` into plain text. | ||
|
|
||
| The generated enum docstrings are already close to user-facing prose, but | ||
| they may contain Sphinx inline roles, line wrapping, or a small known | ||
| codegen defect. Normalize only those differences so the text is suitable | ||
| for error messages. | ||
| """ | ||
| if doc is None: | ||
| return None | ||
| s = doc | ||
| # Known codegen bug on cudaErrorIncompatibleDriverContext. Remove once fixed | ||
| # in cuda-bindings code generation. | ||
| s = s.replace("\n:py:obj:`~.Interactions`", ' "Interactions ') | ||
| # Drop a leading "~." or "." after removing the surrounding RST inline role. | ||
| s = _RST_INLINE_ROLE_RE.sub(lambda m: re.sub(r"^~?\.", "", m.group(1)), s) | ||
| # Strip simple bold emphasis markers. | ||
| s = re.sub(r"\*\*([^*]+)\*\*", r"\1", s) | ||
| # Strip simple italic emphasis markers. | ||
| s = re.sub(r"\*([^*]+)\*", r"\1", s) | ||
| # Collapse wrapped lines and repeated spaces. | ||
| s = re.sub(r"\s+", " ", s).strip() | ||
| s = _fix_hyphenation_wordwrap_spacing(s) | ||
| return s | ||
|
|
||
|
|
||
| class DocstringBackedExplanations: | ||
| """Compatibility shim exposing enum-member ``__doc__`` text via ``dict.get``. | ||
|
|
||
| Keeps the existing ``.get(int(error))`` lookup shape used by ``cuda_utils.pyx``. | ||
| """ | ||
|
|
||
| __slots__ = ("_enum_type",) | ||
|
|
||
| def __init__(self, enum_type: Any) -> None: | ||
| self._enum_type = enum_type | ||
|
|
||
| def get(self, code: int, default: str | None = None) -> str | None: | ||
| try: | ||
| member = self._enum_type(code) | ||
| except ValueError: | ||
| return default | ||
|
|
||
| raw_doc = member.__doc__ | ||
| if raw_doc is None: | ||
| return default | ||
|
|
||
| return clean_enum_member_docstring(raw_doc) | ||
|
|
||
|
|
||
| def get_best_available_explanations( | ||
| enum_type: Any, | ||
| fallback: _ExplanationTable | _ExplanationTableLoader, | ||
| ) -> DocstringBackedExplanations | _ExplanationTable: | ||
| """Pick one explanation source per bindings version. | ||
|
|
||
| Use enum-member ``__doc__`` only for bindings versions known to expose | ||
| usable per-member text (12.9.6+ in the 12.x backport line, 13.2.0+ in the | ||
| 13.x mainline). Otherwise keep using the frozen 13.1.1 fallback tables. | ||
| """ | ||
| if not _binding_version_has_usable_enum_docstrings(_binding_version()): | ||
| if callable(fallback): | ||
| return fallback() | ||
| return fallback | ||
| return DocstringBackedExplanations(enum_type) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems really brittle, but I guess I don't see a way around it. I would prefer to just depend on
rst2txtor something, that but seems unmaintained.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline: what we have here is sufficiently simple, extensively unit tested, and in the worst case we have a cosmetic issue (unwanted sphinx markup in the error messages).
To address the "brittle" concern, I added commit 27aae00
For easy reference, copy-pasting from the commit message:
I believe in combination with the rest of the extensive unit tests, there is very little wiggle room for major issues to go undetected.