Skip to content

Fix unclosed file handles in Converter.parse_graminit_h/_c - #5432

Merged
cobaltt7 merged 1 commit into
psf:mainfrom
kratos0718:fix-conv-unclosed-file-handles
Sep 24, 2026
Merged

cobaltt7 merged 1 commit into
psf:mainfrom
kratos0718:fix-conv-unclosed-file-handles

Conversation

@kratos0718

@kratos0718 kratos0718 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What

blib2to3.pgen2.conv.Converter.parse_graminit_h and .parse_graminit_c each open the grammar .h/.c file with a bare f = open(filename) and then read from f for the rest of the function (a for line in f: loop in the _h case, direct next(f) calls throughout the _c case). Neither method ever closes the handle — no with, no matching .close(), no finally.

Found via codehound, a static analyzer I built; this repo's copy of blib2to3 was part of a broader scan.

Fix

Read the file's lines up front inside a with block, then operate on the resulting list instead of the file object directly:

  • parse_graminit_h: for line in f → for line in lines, where lines comes from with open(filename) as f: lines = f.readlines().
  • parse_graminit_c: same idea, but wrapped in iter(...) since the rest of the function drives itself entirely through next(f) calls (the existing comment even calls this out: # The code below essentially uses f's iterator-ness!) — f = iter(fh.readlines()) preserves that unchanged.

Both changes are scoped to the open(...) line and the following loop header only; the parsing logic itself (all the regex matching, asserts, and table-building in the ~160-line parse_graminit_c body) is untouched, so the diff stays minimal per the norm from #3363/#3794.

Testing

Converter isn't currently exercised by any test in this repo (nothing else in the tree imports it — it looks like a standalone pgen-output-loading utility), so I built a synthetic pgen-format .h/.c fixture pair and drove both methods through it directly:

  • parse_graminit_h / parse_graminit_c both reproduce the exact same symbol2number/number2symbol/dfas/labels/states/start results as before the change, for a minimal one-symbol grammar.
  • The error path (nonexistent file) still returns False and prints the same message.
  • A descriptor-count check before/after a successful parse confirms the handle is actually released now.

Also ran python -c "import blib2to3.pgen2.conv" to confirm no syntax/import issues.

Both methods opened the graminit .h/.c file with a bare open() call
and read it (line-by-line and via next()) for the rest of the
function, without ever closing it - no with block, no matching
close(), and no finally.

Read each file's lines up front inside a with block, then iterate
over the resulting list instead of the file object directly:
parse_graminit_h continues iterating with a plain for loop,
parse_graminit_c wraps the list in iter() to preserve the next(f)
calls it depends on throughout the rest of the function unchanged.

Verified with a synthetic pgen-output fixture exercising both
methods end to end (success path matches the original line-by-line
parse exactly, including all the same symbol/dfa/label/grammar
assertions; error path on a missing file still returns False) and a
descriptor-count check confirming the handle is actually released
after a successful parse.
@cobaltt7 cobaltt7 added the ci: skip news Pull requests that don't need a changelog entry. label Sep 24, 2026
@cobaltt7
cobaltt7 merged commit 601c629 into psf:main Sep 24, 2026
57 of 58 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (f54fe3f) to main (2b96e28):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci: skip news Pull requests that don't need a changelog entry.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants