Repository: numpy/numpy
File: tools/ci/check_c_api_usage.py (Python)

Code before:
  - block comments (/* ... */), even when they span lines
- Prints findings and exits 1 if any issues found, else 0
"""

# List of suspicious function calls:
SUSPICIOUS_FUNCS: tuple[str, ...] = (
    "PyList_GetItem",
    "PyDict_GetItem",
    "PyDict_GetItemWithError",
    "PyDict_GetItemString",
    "PyDict_SetDefault",
    "PyDict_Next",
    "PyWeakref_GetObject",
    "PyWeakref_GET_OBJECT",
    "PyList_GET_ITEM",
    "_PyDict_GetItemStringWithError",
    "PySequence_Fast"
)

# Match any function as a standalone C identifier: (?<!\w)(NAME)(?!\w)
FUNC_RX = re.compile(r"(?<!\w)(?:"
                     + "|".join(map(re.escape, SUSPICIOUS_FUNCS))
                     + r")(?!\w)")

NOQA_OK = "noqa: borrowed-ref OK"
NOQA_MANUAL = "noqa: borrowed-ref - manual fix needed"

DEFAULT_EXTS = {".c", ".h", ".c.src", ".cpp"}
DEFAULT_EXCLUDES = {"pythoncapi-compat"}

def strip_comments(line: str, in_block: bool) -> tuple[str, bool]:
    """
    Return (code_without_comments, updated_in_block).
    Removes // line comments and /* ... */ block comments (non-nesting, C-style).
    """
    i = 0
    out_parts: list[str] = []
    n = len(line)

    while i < n:
        if in_block:
            end = line.find("*/", i)
            if end == -1:
                # Entire remainder is inside a block comment.
                return ("".join(out_parts), True)
            i = end + 2
            in_block = False
            continue

        # Not in block: look for next // or /* from current i
        sl = line.find("//", i)

Review comment (suggestion):
I suppose it is minor, but some of these module-level variables get used directly in functions without passing them in, which is a bit global-like. I wonder if it might make sense to pass them through. I guess it matters less for non-library code--I might be slightly tempted to just pass them through `main` and to the function that uses them from there.

Code after:
    '// noqa: borrowed-ref - manual fix needed'
  - line comments (// ...)
  - block comments (/* ... */), even when they span lines
- Prints findings and exits 1 if any issues found, else 0
"""

def strip_comments(line: str, in_block: bool) -> tuple[str, bool]:
    """
    Return (code_without_comments, updated_in_block).
    Removes // line comments and /* ... */ block comments (non-nesting, C-style).
    """
    i = 0
    out_parts: list[str] = []
    n = len(line)

    while i < n:
        if in_block:
            end = line.find("*/", i)
            if end == -1:
                # Entire remainder is inside a block comment.
                return ("".join(out_parts), True)
            i = end + 2
            in_block = False
            continue

        # Not in block: look for next // or /* from current i
        sl = line.find("//", i)
        bl = line.find("/*", i)

        if sl != -1 and (bl == -1 or sl < bl):
            # Line comment starts first: take code up to '//' and stop
            out_parts.append(line[i:sl])
            return ("".join(out_parts), in_block)

        if bl != -1:
            # Block comment starts: take code up to '/*', then enter block
            out_parts.append(line[i:bl])
            i = bl + 2
            in_block = True
            continue

        # No more comments
        out_parts.append(line[i:])
        break

    return ("".join(out_parts), in_block)

def iter_source_files(root: Path, exts: set[str], excludes: set[str]) -> list[Path]:
    """
    Return a list of source files under 'root', where filenames end with any of the
    extensions in 'exts' (e.g., '.c.src', '.c', '.h').