From 601eac1c25579b994dd3a911c07fbfcaf1a7f9ab Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Wed, 20 May 2026 16:03:14 -0700 Subject: [PATCH] gh-140950: Reset shlex state in push_source() so post-EOF pushes are read After get_token() reaches end of input it sets self.state to None. A subsequent push_source() did not reset state, so the next get_token() call returned EOF immediately and the newly pushed source was silently ignored. pop_source() already resets state to ' ' for the same reason; do the same in push_source(). --- Lib/shlex.py | 3 +++ Lib/test/test_shlex.py | 12 ++++++++++++ .../2026-05-20-16-02-48.gh-issue-140950.3zwzaKTA.rst | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-05-20-16-02-48.gh-issue-140950.3zwzaKTA.rst diff --git a/Lib/shlex.py b/Lib/shlex.py index 5959f52dd12639..49557c134a3e41 100644 --- a/Lib/shlex.py +++ b/Lib/shlex.py @@ -81,6 +81,9 @@ def push_source(self, newstream, newfile=None): self.infile = newfile self.instream = newstream self.lineno = 1 + # Reset state so that a source pushed after EOF (when state is None) + # is still read. Mirrors pop_source(). + self.state = ' ' if self.debug: if newfile is not None: print('shlex: pushing to file %s' % (self.infile,)) diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index 2a355abdeeb30f..93aa01bd94f944 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -368,6 +368,18 @@ def testPunctuationCharsReadOnly(self): with self.assertRaises(AttributeError): shlex_instance.punctuation_chars = False + def testPushSourceAfterEOF(self): + # gh-140950: a source pushed after the current input has reached + # EOF must still be read. Previously get_token() set state to None + # at EOF, and push_source() did not reset it, so the new source + # was silently ignored. + lexer = shlex.shlex('a') + self.assertEqual(lexer.get_token(), 'a') + self.assertEqual(lexer.get_token(), lexer.eof) + lexer.push_source('b') + self.assertEqual(lexer.get_token(), 'b') + self.assertEqual(lexer.get_token(), lexer.eof) + @cpython_only def test_lazy_imports(self): import_helper.ensure_lazy_imports('shlex', {'collections', 're', 'os'}) diff --git a/Misc/NEWS.d/next/Library/2026-05-20-16-02-48.gh-issue-140950.3zwzaKTA.rst b/Misc/NEWS.d/next/Library/2026-05-20-16-02-48.gh-issue-140950.3zwzaKTA.rst new file mode 100644 index 00000000000000..fdb3a5ab6de163 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-20-16-02-48.gh-issue-140950.3zwzaKTA.rst @@ -0,0 +1,4 @@ +Fix :class:`shlex.shlex` ignoring a source pushed via +:meth:`~shlex.shlex.push_source` after the current input had reached EOF. +:meth:`!push_source` now resets the internal lexer state, mirroring +:meth:`~shlex.shlex.pop_source`.