-
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
base: main
Are you sure you want to change the base?
Changes from all commits
4cbe3f3
8b0c526
4fde3d6
7447964
cddb565
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| # 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 typing import Any | ||
|
|
||
| _MIN_12X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (12, 9, 6) | ||
| _MIN_13X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (13, 2, 0) | ||
|
|
||
|
|
||
| # ``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 is a narrow workaround for wrapped forms such as ``non- linear`` that | ||
| would otherwise look awkward in user-facing messages. | ||
| """ | ||
| prev = None | ||
| while prev != s: | ||
| prev = s | ||
| s = re.sub(r"([a-z])- ([a-z])", r"\1-\2", s) | ||
| s = re.sub(r"([a-z]) -([a-z])", r"\1-\2", s) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems too narrow. Why only perform this on lower-case words? |
||
| 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 ') | ||
| s = re.sub( | ||
| r":(?:py:)?(?:obj|func|meth|class|mod|data|const|exc):`([^`]+)`", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This regex seems too specific. Could we just use one that would capture any rst directive?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like (untested): |
||
| lambda m: re.sub(r"^~?\.", "", m.group(1)), | ||
| s, | ||
| ) | ||
| s = re.sub(r"\*\*([^*]+)\*\*", r"\1", s) | ||
| s = re.sub(r"\*([^*]+)\*", r"\1", s) | ||
| s = re.sub(r"\s+", " ", s).strip() | ||
| s = _fix_hyphenation_wordwrap_spacing(s) | ||
| return s | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
|
||
|
|
||
| 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: dict[int, str | tuple[str, ...]] | ||
| ) -> DocstringBackedExplanations | dict[int, str | tuple[str, ...]]: | ||
| """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()): | ||
| return fallback | ||
| return DocstringBackedExplanations(enum_type) | ||
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.
It would be great to move this one level up so this module full of pre-written results doesn't have to be imported at all if we have a new enough
cuda-bindings.