Coverage for cuda/core/_utils/enum_explanations_helpers.py: 96.61%
59 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 01:38 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-13 01:38 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
4"""Internal support for error-enum explanations.
6``cuda_core`` keeps frozen 13.1.1 fallback tables for older ``cuda-bindings``
7releases. Driver/runtime error enums carry usable ``__doc__`` text starting in
8the 12.x backport line at ``cuda-bindings`` 12.9.6, and in the mainline 13.x
9series at ``cuda-bindings`` 13.2.0. This module decides which source to use
10and normalizes generated docstrings so user-facing ``CUDAError`` messages stay
11presentable.
13The cleanup rules here were derived while validating generated enum docstrings
14in PR #1805. Keep them narrow and remove them when codegen quirks or fallback
15support are no longer needed.
16"""
18from __future__ import annotations
20import importlib.metadata
21import re
22from collections.abc import Callable
23from typing import Any
25_MIN_12X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (12, 9, 6)
26_MIN_13X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS = (13, 2, 0)
27_RST_INLINE_ROLE_RE = re.compile(r":(?:[a-z]+:)?[a-z]+:`([^`]+)`")
28_WORDWRAP_HYPHEN_AFTER_RE = re.compile(r"(?<=[0-9A-Za-z_])- (?=[0-9A-Za-z_])")
29_WORDWRAP_HYPHEN_BEFORE_RE = re.compile(r"(?<=[0-9A-Za-z_]) -(?=[0-9A-Za-z_])")
30_ExplanationTable = dict[int, str | tuple[str, ...]]
31_ExplanationTableLoader = Callable[[], _ExplanationTable]
34# ``version.pyx`` cannot be reused here (circular import via ``cuda_utils``).
35def _binding_version() -> tuple[int, int, int]:
36 """Return the installed ``cuda-bindings`` version, or a conservative old value."""
37 try:
38 version = importlib.metadata.version("cuda-bindings")
39 except importlib.metadata.PackageNotFoundError:
40 return (0, 0, 0) # For very old versions of cuda-python
42 parts = version.partition("+")[0].split(".")[:3]
43 parts_int = ([int(v) for v in parts] + [0, 0, 0])[:3]
44 return (parts_int[0], parts_int[1], parts_int[2])
47def _binding_version_has_usable_enum_docstrings(version: tuple[int, int, int]) -> bool:
48 """Whether released bindings are known to carry usable error-enum ``__doc__`` text."""
49 return ( 1aTdefgUVWXYZNOPQbcR
50 _MIN_12X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS <= version < (13, 0, 0)
51 or version >= _MIN_13X_BINDING_VERSION_FOR_ENUM_DOCSTRINGS
52 )
55def _fix_hyphenation_wordwrap_spacing(s: str) -> str:
56 """Remove spaces around hyphens introduced by line wrapping in generated ``__doc__`` text.
58 This targets asymmetric wrap artifacts such as ``non- linear`` or
59 ``GPU- Direct`` while leaving intentional ``a - b`` separators alone.
60 """
61 prev = None 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
62 while prev != s: 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
63 prev = s 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
64 s = _WORDWRAP_HYPHEN_AFTER_RE.sub("-", s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
65 s = _WORDWRAP_HYPHEN_BEFORE_RE.sub("-", s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
66 return s 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
69def clean_enum_member_docstring(doc: str | None) -> str | None:
70 """Turn an enum member ``__doc__`` into plain text.
72 The generated enum docstrings are already close to user-facing prose, but
73 they may contain Sphinx inline roles, line wrapping, or a small known
74 codegen defect. Normalize only those differences so the text is suitable
75 for error messages.
76 """
77 if doc is None: 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKSbc
78 return None 1S
79 s = doc 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
80 # Known codegen bug on cudaErrorIncompatibleDriverContext. Remove once fixed
81 # in cuda-bindings code generation.
82 s = s.replace("\n:py:obj:`~.Interactions`", ' "Interactions ') 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
83 # Drop a leading "~." or "." after removing the surrounding RST inline role.
84 s = _RST_INLINE_ROLE_RE.sub(lambda m: re.sub(r"^~?\.", "", m.group(1)), s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
85 # Strip simple bold emphasis markers.
86 s = re.sub(r"\*\*([^*]+)\*\*", r"\1", s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
87 # Strip simple italic emphasis markers.
88 s = re.sub(r"\*([^*]+)\*", r"\1", s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
89 # Collapse wrapped lines and repeated spaces.
90 s = re.sub(r"\s+", " ", s).strip() 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
91 s = _fix_hyphenation_wordwrap_spacing(s) 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
92 return s 1ijklmnopqrhdefgstuvwxyzABCDEFGHIJKbc
95class DocstringBackedExplanations:
96 """Compatibility shim exposing enum-member ``__doc__`` text via ``dict.get``.
98 Keeps the existing ``.get(int(error))`` lookup shape used by ``cuda_utils.pyx``.
99 """
101 __slots__ = ("_enum_type",)
103 def __init__(self, enum_type: Any) -> None:
104 self._enum_type = enum_type 1aLMNbcR
106 def get(self, code: int, default: str | None = None) -> str | None:
107 try: 1ijklmnopqrhdefgstuLMbc
108 member = self._enum_type(code) 1ijklmnopqrhdefgstuLMbc
109 except ValueError: 1M
110 return default 1M
112 raw_doc = member.__doc__ 1ijklmnopqrhdefgstuLbc
113 if raw_doc is None: 1ijklmnopqrhdefgstuLbc
114 return default 1hL
116 return clean_enum_member_docstring(raw_doc) 1ijklmnopqrhdefgstubc
119def get_best_available_explanations(
120 enum_type: Any,
121 fallback: _ExplanationTable | _ExplanationTableLoader,
122) -> DocstringBackedExplanations | _ExplanationTable:
123 """Pick one explanation source per bindings version.
125 Use enum-member ``__doc__`` only for bindings versions known to expose
126 usable per-member text (12.9.6+ in the 12.x backport line, 13.2.0+ in the
127 13.x mainline). Otherwise keep using the frozen 13.1.1 fallback tables.
128 """
129 if not _binding_version_has_usable_enum_docstrings(_binding_version()): 1aNOPQbcR
130 if callable(fallback): 1OPQ
131 return fallback() 1O
132 return fallback 1PQ
133 return DocstringBackedExplanations(enum_type) 1aNbcR