@@ -35,6 +35,19 @@ def _explanation_text_from_dict_value(value):
3535 return value
3636
3737
38+ def _explanation_dict_text_for_cleaned_doc_compare (value ) -> str :
39+ """Normalize hand-maintained dict text to compare with ``clean_enum_member_docstring`` output.
40+
41+ Dicts follow CUDA header comments (``::cuInit()``-style refs); cleaned enum ``__doc__``
42+ uses plain names after Sphinx role stripping. Strip a leading ``::`` before ``name(`` and
43+ collapse whitespace so both sides use the same conventions as ``clean_enum_member_docstring``.
44+ """
45+ s = _explanation_text_from_dict_value (value )
46+ s = re .sub (r"::([a-zA-Z_][a-zA-Z0-9_]*\()" , r"\1" , s )
47+ s = re .sub (r"\s+" , " " , s ).strip ()
48+ return s
49+
50+
3851def clean_enum_member_docstring (doc : str | None ) -> str | None :
3952 """Turn a FastEnum member ``__doc__`` into plain text for display or fallback logic.
4053
@@ -68,18 +81,20 @@ def clean_enum_member_docstring(doc: str | None) -> str | None:
6881@pytest .mark .parametrize (
6982 ("raw" , "expected" ),
7083 [
71- ("a\n b c" , "a b c" ),
72- (" x \n " , "x" ),
73- (
84+ pytest . param ("a\n b c" , "a b c" , id = "collapse_whitespace " ),
85+ pytest . param (" x \n " , "x" , id = "strip_padding " ),
86+ pytest . param (
7487 "see\n :py:obj:`~.cuInit()` or :py:obj:`cuCtxDestroy()`" ,
7588 "see cuInit() or cuCtxDestroy()" ,
89+ id = "sphinx_py_obj_roles" ,
7690 ),
77- (
91+ pytest . param (
7892 "x :py:func:`~.cudaMalloc()` y" ,
7993 "x cudaMalloc() y" ,
94+ id = "sphinx_py_func_role" ,
8095 ),
81- ("**Note:** text" , "Note: text" ),
82- ("[Deprecated]\n " , "[Deprecated]" ),
96+ pytest . param ("**Note:** text" , "Note: text" , id = "strip_bold " ),
97+ pytest . param ("[Deprecated]\n " , "[Deprecated]" , id = "deprecated_line " ),
8398 ],
8499)
85100def test_clean_enum_member_docstring_examples (raw , expected ):
@@ -92,26 +107,23 @@ def test_clean_enum_member_docstring_none_input():
92107
93108@pytest .mark .xfail (
94109 reason = (
95- "Enum member __doc__ is not byte-identical to explanation dicts in current "
96- "releases (Sphinx/RST and line breaks in __doc__ vs ::-style refs in dicts; "
97- "some deprecated codes use a short [Deprecated] docstring). Remove xfail when "
98- "dicts and generated docstrings share one source of truth."
110+ "Even after clean_enum_member_docstring and dict-side ::/whitespace alignment, "
111+ "some members still differ (e.g. [Deprecated] stub vs full paragraph in dict; "
112+ "wording drift). Remove xfail when dicts and generated docstrings share one source."
99113 ),
100114 strict = False ,
101115)
102116@pytest .mark .parametrize ("module_name,dict_name,enum_type" , _EXPLANATION_MODULES )
103- def test_explanations_dict_matches_enum_member_docstrings (module_name , dict_name , enum_type ):
104- """Each explanation dict value should match the corresponding enum member's __doc__.
117+ def test_explanations_dict_matches_cleaned_enum_docstrings (module_name , dict_name , enum_type ):
118+ """Hand-maintained explanation dict entries should match cleaned enum `` __doc__`` text .
105119
106120 cuda-bindings 13.2+ attaches per-member documentation on driver ``CUresult`` and
107- runtime ``cudaError_t``; this test checks it against the hand-maintained dicts.
108-
109- If this fails, differences may include whitespace, line breaks, Sphinx/RST markup
110- in ``__doc__`` vs raw ``::symbol()`` text in the dicts—normalizing whitespace is
111- a possible follow-up.
121+ runtime ``cudaError_t``. This compares ``clean_enum_member_docstring(member.__doc__)``
122+ to dict text normalized with ``_explanation_dict_text_for_cleaned_doc_compare`` (same
123+ whitespace rules; strip Doxygen ``::`` before ``name(`` to align with Sphinx output).
112124
113- Marked xfail while dict text and generated ``__doc__ `` differ; run
114- ``pytest --runxfail`` on this test to print the full mismatch report .
125+ Marked xfail while mismatches remain; run ``pytest --runxfail `` on this test for the
126+ full mismatch report (normalized dict vs cleaned ``__doc__``) .
115127 """
116128 if _get_binding_version () < _MIN_BINDING_VERSION_FOR_DOCSTRING_COMPARE :
117129 pytest .skip (
@@ -126,24 +138,25 @@ def test_explanations_dict_matches_enum_member_docstrings(module_name, dict_name
126138 for error in enum_type :
127139 code = int (error )
128140 assert code in expl_dict
129- expected = _explanation_text_from_dict_value (expl_dict [code ])
130- actual = error .__doc__
131- if actual is None :
141+ expected = _explanation_dict_text_for_cleaned_doc_compare (expl_dict [code ])
142+ raw_doc = error .__doc__
143+ if raw_doc is None :
132144 continue
145+ actual = clean_enum_member_docstring (raw_doc )
133146 if expected != actual :
134147 mismatches .append ((error , expected , actual ))
135148
136149 if not mismatches :
137150 return
138151
139152 lines = [
140- f"{ len (mismatches )} enum member(s) where dict text != __doc__ (strict equality ):" ,
153+ f"{ len (mismatches )} enum member(s) where normalized dict text != clean_enum_member_docstring(__doc__ ):" ,
141154 ]
142155 for error , expected , actual in mismatches [:15 ]:
143156 lines .append (f" { error !r} " )
144- lines .append (" dict:" )
157+ lines .append (" dict (normalized for compare) :" )
145158 lines .extend (" | " + ln for ln in textwrap .wrap (repr (expected ), width = 100 ) or ["" ])
146- lines .append (" __doc__:" )
159+ lines .append (" cleaned __doc__:" )
147160 lines .extend (" | " + ln for ln in textwrap .wrap (repr (actual ), width = 100 ) or ["" ])
148161 if len (mismatches ) > 15 :
149162 lines .append (f" ... and { len (mismatches ) - 15 } more" )
0 commit comments