11# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
4- """Normalize FastEnum / IntEnum member ``__doc__`` text for user-facing error messages."""
4+ """Internal support for error-enum explanations.
5+
6+ ``cuda_core`` keeps frozen 13.1.1 fallback tables for older ``cuda-bindings``
7+ releases. Starting with ``cuda-bindings`` 13.2.0, driver/runtime error enums
8+ carry usable ``__doc__`` text. This module decides which source to use and
9+ normalizes generated docstrings so user-facing ``CUDAError`` messages stay
10+ close to the long-form explanation prose.
11+
12+ The cleanup rules here were derived while validating docstring-vs-dict parity
13+ in PR #1805. Keep them narrow and remove them when codegen / fallback support is
14+ no longer needed.
15+ """
516
617from __future__ import annotations
718
1223_MIN_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (13 , 2 , 0 )
1324
1425
15- # version.pyx cannot be reused here (circular import)
26+ # `` version.pyx`` cannot be reused here (circular import via ``cuda_utils``).
1627def _binding_version () -> tuple [int , int , int ]:
28+ """Return the installed ``cuda-bindings`` version, or a conservative old value."""
1729 try :
1830 parts = importlib .metadata .version ("cuda-bindings" ).split ("." )[:3 ]
1931 except importlib .metadata .PackageNotFoundError :
@@ -22,7 +34,12 @@ def _binding_version() -> tuple[int, int, int]:
2234
2335
2436def _strip_doxygen_double_colon_prefixes (s : str ) -> str :
25- """Remove Doxygen-style ``::`` before CUDA identifiers (not C++ ``Foo::Bar`` scope)."""
37+ """Remove Doxygen-style ``::`` before CUDA identifiers (not C++ ``Foo::Bar`` scope).
38+
39+ The frozen fallback tables come from CUDA header comments and therefore use
40+ Doxygen ``::name`` references. Generated enum ``__doc__`` text uses Sphinx
41+ roles instead, so parity checks need a small amount of normalization.
42+ """
2643 prev = None
2744 while prev != s :
2845 prev = s
@@ -31,7 +48,11 @@ def _strip_doxygen_double_colon_prefixes(s: str) -> str:
3148
3249
3350def _fix_hyphenation_wordwrap_spacing (s : str ) -> str :
34- """Remove spaces around hyphens introduced by line wrapping in generated ``__doc__`` text."""
51+ """Remove spaces around hyphens introduced by line wrapping in generated ``__doc__`` text.
52+
53+ This is a narrow workaround for wrapped forms such as ``non- linear`` that
54+ otherwise differ from the single-line fallback prose.
55+ """
3556 prev = None
3657 while prev != s :
3758 prev = s
@@ -43,15 +64,17 @@ def _fix_hyphenation_wordwrap_spacing(s: str) -> str:
4364def clean_enum_member_docstring (doc : str | None ) -> str | None :
4465 """Turn an enum member ``__doc__`` into plain text.
4566
46- Collapses whitespace, strips common Sphinx inline roles, fixes wrapped hyphenation,
47- and applies a small codegen workaround for a known bad ``:py:obj`` fragment on
48- ``cudaErrorIncompatibleDriverContext`` (remove when cuda-bindings fixes the source).
67+ The generated enum docstrings are already close to the fallback explanation
68+ prose, but not byte-identical: they may contain Sphinx inline roles, line
69+ wrapping, or a small known codegen defect. Normalize only those differences
70+ so the text is suitable for user-facing error messages.
4971 """
5072 if doc is None :
5173 return None
5274 s = doc
53- # Codegen bug (cudaErrorIncompatibleDriverContext): malformed :py:obj before `with`.
54- # Do not use a raw string for the needle: r"\\n..." would not match a real newline.
75+ # Known codegen bug on cudaErrorIncompatibleDriverContext. Remove once fixed
76+ # in cuda-bindings code generation. Do not use a raw string for the needle:
77+ # r"\n..." would not match the real newline present in __doc__.
5578 s = s .replace ("\n :py:obj:`~.Interactions`" , ' "Interactions ' )
5679 s = re .sub (
5780 r":(?:py:)?(?:obj|func|meth|class|mod|data|const|exc):`([^`]+)`" ,
@@ -66,7 +89,12 @@ def clean_enum_member_docstring(doc: str | None) -> str | None:
6689
6790
6891class DocstringBackedExplanations :
69- """``dict.get``-like lookup over enum-member ``__doc__`` strings."""
92+ """``dict.get``-like lookup over enum-member ``__doc__`` strings.
93+
94+ Once the bindings-version gate says docstrings are available, use them
95+ exclusively. Missing docstrings should surface as ``None`` / ``default``
96+ rather than silently mixing in frozen fallback prose.
97+ """
7098
7199 __slots__ = ("_enum_type" ,)
72100
@@ -89,7 +117,11 @@ def get(self, code: int, default: str | None = None) -> str | None:
89117def get_best_available_explanations (
90118 enum_type : Any , fallback : dict [int , str | tuple [str , ...]]
91119) -> DocstringBackedExplanations | dict [int , str | tuple [str , ...]]:
92- """Use enum-member ``__doc__`` only when cuda-bindings is new enough to provide it."""
120+ """Pick one explanation source per bindings version.
121+
122+ ``cuda-bindings`` < 13.2.0: use the frozen 13.1.1 fallback tables.
123+ ``cuda-bindings`` >= 13.2.0: use enum-member ``__doc__`` exclusively.
124+ """
93125 if _binding_version () < _MIN_BINDING_VERSION_FOR_ENUM_DOCSTRINGS :
94126 return fallback
95127 return DocstringBackedExplanations (enum_type )
0 commit comments