# Find Unused Cython Imports

> Find and remove unused imports/cimports in xpra's Cython .pyx/.pxd files, which ruff and flake8 do not check. Use when asked to find or remove unused imports (especially in .pyx/.pxd/Cython files), clean up imports codebase-wide, or "do more like <commit that removed unused imports>". For pure-Python .py files, use `ruff check --select F401` instead.

- Skill: `xpra-org/find-unused-cython-imports` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add xpra-org/find-unused-cython-imports`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xpra-org/find-unused-cython-imports/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: xpra-org (https://skillmd.com/u/xpra-org)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xpra-org/find-unused-cython-imports

---


# find-unused-cython-imports

`ruff`/`flake8` only lint `.py` files, so unused `import`/`cimport` statements in xpra's ~117 `.pyx` and ~32 `.pxd` Cython files go undetected. This skill finds them with a bundled scanner and — crucially — **verifies every removal by transpiling with `cython`**, which does full name resolution without needing any native libraries.

Bundled scripts (in this skill's directory):
- `find_unused_cimports.py` — detector (heuristic).
- `rewrite.py` — removes named imports from statements (single-line + multi-line paren blocks), then you re-verify.

## First: the two traps that cause false positives

A naive "does the name appear elsewhere?" check is wrong in two ways here. The bundled scanner already handles both — do not reimplement the check without them:

1. **`#` inside a string literal is not a comment.** Log lines like
   `log("resource %#x freed", <uintptr_t> ptr)` contain `#` inside `"%#x"`.
   Splitting the line on `#` hides the real `<uintptr_t>` usage after it, so the
   type looks unused when it is not. Truncate a line at `#` only when the `#` is
   outside quotes.
2. **A `.pxd` shares its namespace with the companion `.pyx`.** A name cimported
   in `foo.pxd` may be used only in `foo.pyx` (which relies on it instead of
   self-importing). Removing it from the `.pxd` breaks the build. Count
   non-import usage in the companion `.pyx` too, and also check that no other
   module does `from xpra.<this_module> cimport <name>` (re-export).

## The verification oracle

`cython` transpilation (`.pyx` → `.c`) resolves every C-level name and **errors
on a wrongly-removed `cimport`** (`'<name>' is not a type identifier`,
`undeclared name`, …). It does **not** need the native libs, so it works even for
nvenc / amf / vpx / avif / wayland files:

```sh
cython -X language_level=3 -I . <file>.pyx -o /tmp/out.c
```

Transpilation does **not** validate runtime Python `import`s — an undefined
Python name only raises `NameError` at runtime, not at transpile time. So Python
imports are covered by the scanner's static token scan, not by transpilation.

## Workflow

Run from the repo root.

### 1. Baseline — confirm each candidate file transpiles cleanly *before* editing

```sh
SKILL=.claude/skills/find-unused-cython-imports
TSV=1 python3 $SKILL/find_unused_cimports.py xpra > /tmp/candidates.tsv
cut -f1 /tmp/candidates.tsv | sort -u | while read f; do
  [ "${f##*.}" = pyx ] || continue
  cython -X language_level=3 -I . "$f" -o /tmp/_b.c >/dev/null 2>&1 || echo "PRE-FAIL $f"
done
```
Any file that already fails to transpile can't be used as an oracle — set it aside and inspect by hand.

### 2. Review the candidate list

Skim `/tmp/candidates.tsv`. Delete any rows you want to keep (e.g. an import
kept deliberately for a side effect, or one you're unsure about). The scanner is
a heuristic; you are the gate.

### 3. Remove

```sh
python3 $SKILL/rewrite.py /tmp/candidates.tsv
```

### 4. Verify — transpile every edited `.pyx`, and every consumer of an edited `.pxd`

```sh
for f in $(git diff --name-only -- '*.pyx'); do
  cython -X language_level=3 -I . "$f" -o /tmp/_v.c >/tmp/err.txt 2>&1 \
    || { echo "FAIL $f"; tail -5 /tmp/err.txt; }
done
```
A `.pxd` can't be transpiled alone: transpile the `.pyx` files in its module
(its companion `.pyx` and any file that `cimport`s from it). If a file fails,
the removed name was actually used — `git checkout -- <file>` it and drop that
row.

Independently confirm the Python-level imports (not covered by transpile): every
removed name should have **zero** non-import, comment-stripped occurrences in its
edited file. Re-running `find_unused_cimports.py` on the edited tree should
report `0 candidate unused imports` (idempotent).

### 5. Tidy & commit

Whole-line removals can leave a doubled blank line; collapse any blank run your
edit newly created (vs `HEAD`) down to one. Then confirm scope and commit
straight to master (this repo's convention — no throw-away branch):

```sh
ruff check ./xpra --ignore E902 --ignore E501       # .py side unaffected
git diff --name-only | grep -v '\.pyx$\|\.pxd$'      # expect: nothing else
git add -- '*.pyx' '*.pxd' && git commit
```

## Notes

- The scanner keys "used" on the bound local name (respects `import x as y`).
- Multi-line parenthesised `cimport (...)` blocks and `\`-continued lines are
  parsed as one statement.
- Names appearing only inside a `#`-comment or a string literal count as *not*
  used for a comment but *as* used for a string (conservative — avoids removing
  something a string references). A name used only in a commented-out line is
  therefore reported as unused; that's usually correct (the code is dead), but
  eyeball it.

