|
3 | 3 |
|
4 | 4 | import importlib |
5 | 5 | import importlib.metadata |
| 6 | +import re |
6 | 7 | import textwrap |
7 | 8 |
|
8 | 9 | import pytest |
@@ -34,6 +35,61 @@ def _explanation_text_from_dict_value(value): |
34 | 35 | return value |
35 | 36 |
|
36 | 37 |
|
| 38 | +def clean_enum_member_docstring(doc: str | None) -> str | None: |
| 39 | + """Turn a FastEnum member ``__doc__`` into plain text for display or fallback logic. |
| 40 | +
|
| 41 | + Always: collapse all whitespace (including newlines) to single spaces and strip ends. |
| 42 | +
|
| 43 | + Best-effort: remove common Sphinx/reST inline markup seen in generated CUDA docs, |
| 44 | + e.g. ``:py:obj:`~.cudaGetLastError()` `` -> ``cudaGetLastError()`` (relative ``~.`` is |
| 45 | + dropped). Does not aim for perfect reST parsing—only patterns that appear on these |
| 46 | + enums in practice. |
| 47 | +
|
| 48 | + Returns ``None`` if ``doc`` is ``None``; otherwise returns a non-empty or empty str. |
| 49 | + """ |
| 50 | + if doc is None: |
| 51 | + return None |
| 52 | + s = doc |
| 53 | + # Sphinx roles with a single backtick-delimited target (most common on these enums). |
| 54 | + # Strip the role and keep the inner text; drop leading ~. used for same-module refs. |
| 55 | + s = re.sub( |
| 56 | + r":(?:py:)?(?:obj|func|meth|class|mod|data|const|exc):`([^`]+)`", |
| 57 | + lambda m: re.sub(r"^~?\.", "", m.group(1)), |
| 58 | + s, |
| 59 | + ) |
| 60 | + # Inline emphasis / strong (rare in error blurbs) |
| 61 | + s = re.sub(r"\*\*([^*]+)\*\*", r"\1", s) |
| 62 | + s = re.sub(r"\*([^*]+)\*", r"\1", s) |
| 63 | + # Collapse whitespace (newlines -> spaces) and trim |
| 64 | + s = re.sub(r"\s+", " ", s).strip() |
| 65 | + return s |
| 66 | + |
| 67 | + |
| 68 | +@pytest.mark.parametrize( |
| 69 | + ("raw", "expected"), |
| 70 | + [ |
| 71 | + ("a\nb c", "a b c"), |
| 72 | + (" x \n ", "x"), |
| 73 | + ( |
| 74 | + "see\n:py:obj:`~.cuInit()` or :py:obj:`cuCtxDestroy()`", |
| 75 | + "see cuInit() or cuCtxDestroy()", |
| 76 | + ), |
| 77 | + ( |
| 78 | + "x :py:func:`~.cudaMalloc()` y", |
| 79 | + "x cudaMalloc() y", |
| 80 | + ), |
| 81 | + ("**Note:** text", "Note: text"), |
| 82 | + ("[Deprecated]\n", "[Deprecated]"), |
| 83 | + ], |
| 84 | +) |
| 85 | +def test_clean_enum_member_docstring_examples(raw, expected): |
| 86 | + assert clean_enum_member_docstring(raw) == expected |
| 87 | + |
| 88 | + |
| 89 | +def test_clean_enum_member_docstring_none_input(): |
| 90 | + assert clean_enum_member_docstring(None) is None |
| 91 | + |
| 92 | + |
37 | 93 | @pytest.mark.xfail( |
38 | 94 | reason=( |
39 | 95 | "Enum member __doc__ is not byte-identical to explanation dicts in current " |
|
0 commit comments